本文主要是介绍pygame 实现三体运动,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
pygame 实现三体运动
- 效果
- 实现方法
- 预备知识
- 1.向量
- 2.合力的计算
- 3.引力的计算
- 4.python的运用
- 开始
- 系统(system)
- 物体(object)
- 计算
- 显示
- 运行
- 进阶
效果
V1.0.10版
新增添加物体功能
移除物体功能
稍微加快了一点点
实现方法
对于N体模拟,可以这样思考
- 计算出一个系统中每一个对象(物体Object)的受力情况
- 根据受力情况计算出每一个物体受的合力
- 该物体下一帧运动的速度和方向就是所受合力的大小和方向
通过这种方法,就可以简单的模拟出N体运动
预备知识
1.向量
向量,也称矢量,是一种具有大小和方向的量
它可以用一个箭头表示
在坐标系中,可以表示为
a[3.1]
[-2.9]
涉及向量的原因是力本身就表示为一个向量
例如:
一个苹果受到10N的重力,在这里10N是力的大小,而方向则是竖直向下
2.合力的计算
合力的计算其实也就是向量的加法
根据平行四边形法则:
图甲中F1和F2的合力为F
如果
F1表示为[100,0]
F2表示为[10,60]
那么F就可以表示为[110,60]
至于图乙其实与图甲是一样的
那么,问题来了,物体的受力情况不一定仅有两个啊,怎么办呢?
其实,我们可以两个两个力的算,直到把所有力都算完,就得到了一个最终的合力了
3.引力的计算
两个可看作质点的物体之间的万有引力,可以用以下公式计算:
即万有引力等于引力常量乘以两物体质量的乘积 除以它们距离的平方。
也就是说,引力与两物体之间距离的平方成反比,
而与两物体质量的乘积成正比
说人话就是靠的近引力就大,靠的远引力就小
在同样的距离时,质量越大引力越大,反之亦然
4.python的运用
你需要拥有这些:
- python的基本语法
- pygame库的用法
- 一个IDE,所谓磨刀不误砍柴工,个人推荐PyCharm
- 以及…伊文斯的介绍信,嗯,很好,组织没有看错你 ^ _ ^
开始
所有的代码都在最后
先从头开始吧:
注意,如果你对 类(class)不熟悉,那请点
系统(system)
总要有一个系统来容纳一切吧!
class System(object): # 继承object类name = "" objects = [] # 所有的对象(物体)都在这里
非常简单的一个类,不过还应该来点东西
def __init__(self, name):self.name = name # 在建立系统的时候有一个名字的!def add_object(self, obj): # 添加物体self.objects.append(obj)
嗯,基本的东西以及好了,下面需要一个可以制造物体的神了!
物体(object)
class Objects(object):mass = 0 # 质量power = [] # 受到的力,矢量[大小,方向]speed = [] # 加速度,矢量[大小,方向]position = [] # 位置position_ = [] # 最初位置move_to = [] # 下一帧移动的距离,方向positions = [] # 所有的点volume = 0 # 体积color = [] # 颜色name = "" # 名字def __init__(self, name, mass, speed, position, volume, color):self.mass = massself.position = positionself.volume = volumeself.speed = speedself.color = colorself.name = nameself.positions.append(position[:])self.position_ = position[:]
这一次要多一点,但都很简单,在创作一个物体时当然需要很多数据!
计算
到目前为止,我们拥有了一个系统,拥有了可以创造物体的神
但它们不会动,这显然是无聊的,于是在系统中加入了
def work_power(self):for obj in self.objects: # 遍历系统中的所有物体obj.power = [] # 把它们的受力情况清空if obj.speed == [0, 0]: # 如果加速度为0,则跳过passelse: # 否则将加速度变成力(物体的加速度可以看作一种力(根据F=am))obj.speed[0] = obj.speed[0] * obj.massobj.speed[1] = obj.speed[1] * obj.massobj.power.append(obj.speed) # 把惯性力添加到受力情况中for o in self.objects: # 遍历其他物体v0 = o.position[0] - obj.position[0] # 算x轴上的距离v1 = o.position[1] - obj.position[1] # 算y轴上的距离distance_2 = v0 ** 2 + v1 ** 2 # 算距离,根据勾股定理if distance_2 == 0: # 如果距离为0,则不受力passelse:v0 *= G/distance_2*(obj.mass*o.mass) # 根据万有引力公式算引力向量v1 *= G/distance_2*(obj.mass*o.mass)obj.power.append([v0, v1]) # 将引力添加到改物体的受力情况中
上面的代码算出了每个物体的受力情况
但合力却没有算…
那就再来个方法把!(在system类中)
def work_force(self):for obj in self.objects: # 依然是遍历每个物体for n in range(len(obj.power) - 1): # 遍历物体所受的每一个力f1 = obj.power[0] # 把它们取出来f2 = obj.power[1]f_t = [f1[0]+f2[0], f1[1]+f2[1]] # 根据合力的算法将合力算出来del obj.power[0] # 把算过的的力删掉del obj.power[0]obj.power.append(f_t) # 把合力添加进去obj.position[0] += obj.power[0][0]/obj.mass # 将物体的位置沿合力的方向移动obj.position[1] += obj.power[0][1]/obj.massobj.speed = [obj.power[0][0]/obj.mass, obj.power[0][1]/obj.mass] #将合力变为加速度
OK,计算部分也完成了
但,我们看不见啊,看不见慌的很啊!
显示
轨道的实现其实是pygame中aalines方法的作用,
通过收集最近经过的点绘制 “曲线”
def display(self, scr, show_orbit, length=500): # 接受3个参数,scr就是pygame的screen, show_orbit则是是否显示轨道,length就是轨道的长度for obj in self.objects: # 遍历遍历pygame.draw.circle(scr, obj.color, (int(obj.position[0]), int(obj.position[1])), obj.volume) # 给我画下来!if show_orbit: # 如果要显示的话obj.positions = obj.positions[:]obj.positions.insert(0, [int(obj.position[0]), int(obj.position[1])]) # 收集点pygame.draw.aalines(scr, obj.color, False, obj.positions, 3) # 画轨道if len(obj.positions) > length: # 让轨道在长度之后消失del obj.positions[-1]elif len(obj.positions) > len(self.objects): # 解决一个互传问题if obj.position_ in obj.positions:for n in range(len(self.objects)):del obj.positions[-1]
除了图像,我们还需要一些数据:
def display_data(self, scr, c):font = pygame.font.SysFont("Consolas", 25)fps = font.render("FPS: "+str(round(c.get_fps(), 2)), False, (255, 255, 255))scr.blit(fps, (50, 50))if self.objects:for index, obj in enumerate(self.objects):pos = font.render(obj.name+": "+str(int(obj.position[0])) + " " +str(int(obj.position[1])), False, (255, 255, 255))scr.blit(pos, (50, 80+index*35))
这里不再过多解释,可以自行百度
运行
完成一切后,
就可以疯狂的调用了
来一波四体运动!
universe = System("Earth University")earth = Objects("Earth", 1000, [0, 0], [200, 200], 5, [115, 15, 249])
sun = Objects("Sun", 1000, [0, 0], [500, 600], 5, [255, 0, 0])
moon = Objects("Moon", 1000, [0, 0], [500, 300], 5, [0, 255, 0])
mars = Objects("Mars", 1000, [0, 0], [600, 400], 5, [15, 215, 249])universe.add_object(earth)
universe.add_object(sun)
universe.add_object(moon)
universe.add_object(mars)universe.work_power()
universe.work_force()pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode([1500, 1200])
pygame.display.set_caption("Force")keep = Truewhile keep:for event in pygame.event.get():if event.type == pygame.QUIT:keep = Falsescreen.fill((0, 0, 0))universe.work_power()universe.work_force()universe.display(screen, True)universe.display_data(screen, clock)pygame.display.update()clock.tick(200)pygame.quit()
是不是超级简单啊?
OK,全部代码:
import pygame
"""
万有引力公式:
F = G * (m1*m2)/r**2
其中 F 为受某力的大小,m1,m2分别为两物体的质量,r为距离
牛顿第二定律:
F = am
a为加速度
m为物体质量
推导公式:
a = F/m
m = F/a
"""global G, PI
G = 6.754*0.00001
PI = 3.14159265358979323class System(object):name = ""objects = []def __init__(self, name):self.name = namedef add_object(self, obj):self.objects.append(obj)def work_power(self):for obj in self.objects:obj.power = []if obj.speed == [0, 0]:passelse:obj.speed[0] = obj.speed[0] * obj.massobj.speed[1] = obj.speed[1] * obj.massobj.power.append(obj.speed)for o in self.objects:v0 = o.position[0] - obj.position[0]v1 = o.position[1] - obj.position[1]distance_2 = v0 ** 2 + v1 ** 2if distance_2 == 0:passelse:v0 *= G/distance_2*(obj.mass*o.mass)v1 *= G/distance_2*(obj.mass*o.mass)obj.power.append([v0, v1])def work_force(self):for obj in self.objects:for n in range(len(obj.power) - 1):f1 = obj.power[0]f2 = obj.power[1]f_t = [f1[0]+f2[0], f1[1]+f2[1]]del obj.power[0]del obj.power[0]obj.power.append(f_t)obj.position[0] += obj.power[0][0]/obj.massobj.position[1] += obj.power[0][1]/obj.massobj.speed = [obj.power[0][0]/obj.mass, obj.power[0][1]/obj.mass]# print(obj.speed)def display(self, scr, show_orbit, length=500):for obj in self.objects:pygame.draw.circle(scr, obj.color, (int(obj.position[0]), int(obj.position[1])), obj.volume)if show_orbit:obj.positions = obj.positions[:]obj.positions.insert(0, [int(obj.position[0]), int(obj.position[1])])pygame.draw.aalines(scr, obj.color, False, obj.positions, 3)if len(obj.positions) > length:del obj.positions[-1]elif len(obj.positions) > len(self.objects):if obj.position_ in obj.positions:for n in range(len(self.objects)):del obj.positions[-1]def display_data(self, scr, c):font = pygame.font.SysFont("Consolas", 25)fps = font.render("FPS: "+str(round(c.get_fps(), 2)), False, (255, 255, 255))scr.blit(fps, (50, 50))if self.objects:for index, obj in enumerate(self.objects):pos = font.render(obj.name+": "+str(int(obj.position[0])) + " " +str(int(obj.position[1])), False, (255, 255, 255))scr.blit(pos, (50, 80+index*35))class Objects(object):mass = 0 # 质量power = [] # 受到的力,矢量[大小,方向]speed = [] # 初速度,矢量[大小,方向]position = [] # 位置position_ = [] # 最初位置move_to = [] # 下一帧移动的距离,方向positions = [] # 所有的点volume = 0 # 体积color = [] # 颜色name = "" # 名字def __init__(self, name, mass, speed, position, volume, color):self.mass = massself.position = positionself.volume = volumeself.speed = speedself.color = colorself.name = nameself.positions.append(position[:])self.position_ = position[:]universe = System("Earth University")earth = Objects("Earth", 1000, [0, 0], [200, 200], 5, [115, 15, 249])
sun = Objects("Sun", 1000, [0, 0], [500, 600], 5, [255, 0, 0])
moon = Objects("Moon", 1000, [0, 0], [500, 300], 5, [0, 255, 0])
mars = Objects("Mars", 1000, [0, 0], [600, 400], 5, [15, 215, 249])universe.add_object(earth)
universe.add_object(sun)
universe.add_object(moon)
universe.add_object(mars)universe.work_power()
universe.work_force()pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode([1500, 1200])
pygame.display.set_caption("Force")keep = Truewhile keep:for event in pygame.event.get():if event.type == pygame.QUIT:keep = Falsescreen.fill((0, 0, 0))universe.work_power()universe.work_force()universe.display(screen, True)universe.display_data(screen, clock)pygame.display.update()clock.tick(200)pygame.quit()
进阶
要实现V.1.0.10版就要添加功能:添加和移除物体
实际上就是添加一个方法:
def set_object(self, scr, k):def draw_mesh(w, h, e, scr_, color): # 画网格for n in range(0, w, e):pygame.draw.line(scr_, color, (n, 0), (n, h))for n in range(0, h, e):pygame.draw.line(scr_, color, (0, n), (w, n))for n in range(0, w, e * 10):pygame.draw.line(scr_, (255, 255, 255), (n, 0), (n, h), 1)for n in range(0, h, e * 10):pygame.draw.line(scr_, (255, 255, 255), (0, n), (w, n), 1)def draw_mouse(scr_):pygame.draw.line(scr_, (255, 0, 0), (x, 0), (x, 800), 2)pygame.draw.line(scr_, (255, 0, 0), (0, y), (1000, y), 2)def display_data(scr_, mx, my):font_ = pygame.font.SysFont("Consolas", 25)pos = font_.render("Pos: " + str(mx) + " " + str(my), False, (255, 255, 255))scr_.blit(pos, (50, 50))def adding_mode(x_, y_):def name_it():self.name_ = input("Please name the object")named = Thread(target=name_it)named.start()while True:for event__ in pygame.event.get():if event__.type == pygame.QUIT:breakscr.fill((0, 0, 0))pygame.mouse.set_visible(True)# 画数据font_ = pygame.font.SysFont("Consolas", 25)fun_ = font_.render("Enter Object's name", False, (155, 155, 255))scr.blit(fun_, (550, 50))if self.name_ != "":new_object = Objects(self.name_, 1000, [0, 0], [x_, y_], 5, [random.randint(55, 255),random.randint(55, 255),random.randint(55, 255)])self.add_object(new_object)self.name_ = ""breakif k[pygame.K_a]:while True:for event_ in pygame.event.get():if event_.type == pygame.QUIT:breakscr.fill((0, 0, 0))pygame.mouse.set_visible(False) # 隐藏鼠标# 画网格x, y = pygame.mouse.get_pos()screen.fill((0, 0, 0))draw_mesh(1000, 800, 20, screen, (110, 110, 110))draw_mouse(scr)display_data(scr, x, y)# 画数据font = pygame.font.SysFont("Consolas", 25)fun = font.render("Press 'q' to stop adding", False, (155, 155, 255))scr.blit(fun, (550, 50))k = pygame.key.get_pressed()if pygame.mouse.get_pressed()[0] == 1:adding_mode(x, y)# 退出添加模式if k[pygame.K_q]:pygame.mouse.set_visible(True) # 显示鼠标breakpygame.display.update()elif k[pygame.K_m]:f = Falseindex = -1def name_it_():self.name_ = input("Please enter the object's name")named_ = Thread(target=name_it_)named_.start()while True:for event___ in pygame.event.get():if event___.type == pygame.QUIT:breakscr.fill((0, 0, 0))pygame.mouse.set_visible(True)# 画数据font__ = pygame.font.SysFont("Consolas", 25)fun__ = font__.render("Enter Object's name", False, (155, 155, 255))scr.blit(fun__, (550, 50))if self.name_ != "":for n_ in self.objects:index += 1if n_.name == self.name_:f = Truedel self.objects[index]if f:print("Find it and remove it!")else:print("None!")self.name_ = ""breakpygame.display.update()
代码很好理解,就不再赘述
Ok,放V1.0.10版全部代码
import pygame, random
from threading import Thread
"""
万有引力公式:
F = G * (m1*m2)/r**2
其中 F 为受某力的大小,m1,m2分别为两物体的质量,r为距离
牛顿第二定律:
F = am
a为加速度
m为物体质量
推导公式:
a = F/m
m = F/a
"""
G, PI = 6.754*0.00001, 3.14159265358979323class System(object):name = ""objects = []name_ = ""def __init__(self, name):self.name = namedef add_object(self, obj):self.objects.append(obj)def work_power(self):for obj in self.objects:obj.power = []if obj.speed == [0, 0]:passelse:obj.speed[0] = obj.speed[0] * obj.massobj.speed[1] = obj.speed[1] * obj.massobj.power.append(obj.speed)for o in self.objects:v0 = o.position[0] - obj.position[0]v1 = o.position[1] - obj.position[1]distance_2 = v0 ** 2 + v1 ** 2if distance_2 == 0:passelse:v0 *= G/distance_2*(obj.mass*o.mass)v1 *= G/distance_2*(obj.mass*o.mass)obj.power.append([v0, v1])def work_force(self):for obj in self.objects:for n in range(len(obj.power) - 1):f1 = obj.power[0]f2 = obj.power[1]f_t = [f1[0]+f2[0], f1[1]+f2[1]]del obj.power[0]del obj.power[0]obj.power.append(f_t)obj.position[0] += obj.power[0][0]/obj.massobj.position[1] += obj.power[0][1]/obj.massobj.speed = [obj.power[0][0]/obj.mass, obj.power[0][1]/obj.mass]# print(obj.speed)def display(self, scr, show_orbit, length=500):for obj in self.objects:pygame.draw.circle(scr, obj.color, (int(obj.position[0]), int(obj.position[1])), obj.volume)if show_orbit:obj.positions = obj.positions[:]obj.positions.insert(0, [int(obj.position[0]), int(obj.position[1])])pygame.draw.aalines(scr, obj.color, False, obj.positions, 3)if len(obj.positions) > length:del obj.positions[-1]elif len(obj.positions) > len(self.objects):if obj.position_ in obj.positions:for n in range(len(self.objects)):del obj.positions[-1]def display_data(self, scr, c):font = pygame.font.SysFont("Consolas", 25)fps = font.render("FPS: "+str(round(c.get_fps(), 2)), False, (255, 255, 255))fun = font.render("Press 'a' to add a object", False, (155, 155, 255))scr.blit(fun, (550, 50))fun = font.render("Press 'm' to remove a object", False, (155, 155, 255))scr.blit(fun, (550, 100))scr.blit(fps, (50, 50))if self.objects:for index, obj in enumerate(self.objects):pos = font.render(obj.name+": "+str(int(obj.position[0])) + " " +str(int(obj.position[1])), False, (255, 255, 255))scr.blit(pos, (50, 80+index*35))def set_object(self, scr, k):def draw_mesh(w, h, e, scr_, color):for n in range(0, w, e):pygame.draw.line(scr_, color, (n, 0), (n, h))for n in range(0, h, e):pygame.draw.line(scr_, color, (0, n), (w, n))for n in range(0, w, e * 10):pygame.draw.line(scr_, (255, 255, 255), (n, 0), (n, h), 1)for n in range(0, h, e * 10):pygame.draw.line(scr_, (255, 255, 255), (0, n), (w, n), 1)def draw_mouse(scr_):pygame.draw.line(scr_, (255, 0, 0), (x, 0), (x, 800), 2)pygame.draw.line(scr_, (255, 0, 0), (0, y), (1000, y), 2)def display_data(scr_, mx, my):font_ = pygame.font.SysFont("Consolas", 25)pos = font_.render("Pos: " + str(mx) + " " + str(my), False, (255, 255, 255))scr_.blit(pos, (50, 50))def adding_mode(x_, y_):def name_it():self.name_ = input("Please name the object")named = Thread(target=name_it)named.start()while True:for event__ in pygame.event.get():if event__.type == pygame.QUIT:breakscr.fill((0, 0, 0))pygame.mouse.set_visible(True)# 画数据font_ = pygame.font.SysFont("Consolas", 25)fun_ = font_.render("Enter Object's name", False, (155, 155, 255))scr.blit(fun_, (550, 50))if self.name_ != "":new_object = Objects(self.name_, 1000, [0, 0], [x_, y_], 5, [random.randint(55, 255),random.randint(55, 255),random.randint(55, 255)])self.add_object(new_object)self.name_ = ""breakif k[pygame.K_a]:while True:for event_ in pygame.event.get():if event_.type == pygame.QUIT:breakscr.fill((0, 0, 0))pygame.mouse.set_visible(False) # 隐藏鼠标# 画网格x, y = pygame.mouse.get_pos()screen.fill((0, 0, 0))draw_mesh(1000, 800, 20, screen, (110, 110, 110))draw_mouse(scr)display_data(scr, x, y)# 画数据font = pygame.font.SysFont("Consolas", 25)fun = font.render("Press 'q' to stop adding", False, (155, 155, 255))scr.blit(fun, (550, 50))k = pygame.key.get_pressed()if pygame.mouse.get_pressed()[0] == 1:adding_mode(x, y)# 退出添加模式if k[pygame.K_q]:pygame.mouse.set_visible(True) # 显示鼠标breakpygame.display.update()elif k[pygame.K_m]:f = Falseindex = -1def name_it_():self.name_ = input("Please enter the object's name")named_ = Thread(target=name_it_)named_.start()while True:for event___ in pygame.event.get():if event___.type == pygame.QUIT:breakscr.fill((0, 0, 0))pygame.mouse.set_visible(True)# 画数据font__ = pygame.font.SysFont("Consolas", 25)fun__ = font__.render("Enter Object's name", False, (155, 155, 255))scr.blit(fun__, (550, 50))if self.name_ != "":for n_ in self.objects:index += 1if n_.name == self.name_:f = Truedel self.objects[index]if f:print("Find it and remove it!")else:print("None!")self.name_ = ""breakpygame.display.update()class Objects(object):mass = 0 # 质量power = [] # 受到的力,矢量[大小,方向]speed = [] # 初速度,矢量[大小,方向]position = [] # 位置position_ = [] # 最初位置move_to = [] # 下一帧移动的距离,方向positions = [] # 所有的点volume = 0 # 体积color = [] # 颜色name = "" # 名字def __init__(self, name, mass, speed, position, volume, color):self.mass = massself.position = positionself.volume = volumeself.speed = speedself.color = colorself.name = nameself.positions.append(position[:])self.position_ = position[:]universe = System("Earth University")universe.work_power()
universe.work_force()pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode([1000, 800])
pygame.display.set_caption("Force")keep = Truewhile keep:for event in pygame.event.get():if event.type == pygame.QUIT:keep = Falsescreen.fill((0, 0, 0))universe.work_power()universe.work_force()universe.display(screen, True)universe.display_data(screen, clock)pygame.display.update()clock.tick(200)key = pygame.key.get_pressed()universe.set_object(screen, key)pygame.quit()
转载请声明来处.
这篇关于pygame 实现三体运动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!