提交 dd072106 编写于 作者: 三月三net's avatar 三月三net

Python超人-宇宙模拟器

上级 7bc08933
......@@ -45,8 +45,6 @@ class Body(metaclass=ABCMeta):
self.__his_vel = []
self.__his_acc = []
self.__his_reserved_num = 200
# 是否忽略质量(如果为True,则不计算引力)
self.ignore_mass = ignore_mass
if name is None:
name = getattr(self.__class__, '__name__')
......@@ -54,6 +52,12 @@ class Body(metaclass=ABCMeta):
self.name = name
self.__mass = mass
if self.__mass <= 0: # 质量小于等于0就忽略
self.ignore_mass = True
else:
# 是否忽略质量(如果为True,则不计算引力)
self.ignore_mass = ignore_mass
self.__init_position = None
self.__init_velocity = None
......@@ -92,7 +96,19 @@ class Body(metaclass=ABCMeta):
self.__has_rings = False
def set_light_disable(self, value):
def set_ignore_mass(self, value=True):
"""
设置忽略质量,True为引力失效
@param value:
@return:
"""
if self.__mass <= 0: # 质量小于等于0就忽略
self.ignore_mass = True
else:
self.ignore_mass = value
return self
def set_light_disable(self, value=True):
"""
设置灯光为无效
@param value:
......@@ -311,6 +327,9 @@ class Body(metaclass=ABCMeta):
"""
天体的体积(单位:km³)
"""
if self.mass <= 0:
# 质量为0或者负数,就给一个虚拟体积数
return 1e10
# v = m/ρ
# 体积(m³) = 质量(kg) / 密度(kg/m³)
# 体积(km³) = 体积(m³) / 1e9
......
......@@ -24,4 +24,7 @@ if __name__ == '__main__':
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, 60, position=(0, e.raduis + 500, -4500), show_trail=True, view_closely=0.001)
ursina_run(bodies, 60, position=(0, e.raduis + 500, -4500),
show_trail=True,
show_timer=True,
view_closely=0.001)
......@@ -64,6 +64,7 @@ def ursina_run(bodies,
show_grid=True,
show_trail=False,
show_name=False,
show_timer=False,
save_as_json=None,
view_closely=False):
"""
......@@ -76,12 +77,14 @@ def ursina_run(bodies,
@param show_grid: 是否显示空间网格
@param show_trail: 是否显示拖尾
@param show_name: 是否显示天体名称
@param show_timer: 是否显示计时器
@param save_as_json: 将所有天体的信息保存为 json 文件
@param view_closely: 是否近距离查看天体
@return:
"""
from simulators.ursina_simulator import UrsinaSimulator, UrsinaPlayer
from simulators.ursina_simulator import UrsinaSimulator
from simulators.ursina.entities.ursina_player import UrsinaPlayer
body_sys = System(bodies)
if show_name:
......@@ -100,7 +103,6 @@ def ursina_run(bodies,
view_azimuth = 0 # 暂时未用
player = UrsinaPlayer(position, view_azimuth, simulator.ursina_views)
def callback_update():
UrsinaEvent.on_application_run()
for ursina_view in simulator.ursina_views:
......@@ -116,6 +118,7 @@ def ursina_run(bodies,
simulator.run(dt,
cosmic_bg=cosmic_bg,
show_grid=show_grid,
show_timer=show_timer,
bg_music=bg_music,
view_closely=view_closely)
......
# -*- coding:utf-8 -*-
# title :太阳系场景模拟1
# description :太阳系场景模拟(展示的效果为太阳系真实的距离)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Moon, Asteroids, Body
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_YEAR, AU
from sim_scenes.func import mayavi_run, ursina_run
if __name__ == '__main__':
# 八大行星:木星(♃)、土星(♄)、天王星(♅)、海王星(♆)、地球(⊕)、金星(♀)、火星(♂)、水星(☿)
# 排列顺序
# 1、体积:(以地球为1)木星 :土星 :天王星 :海王星 :地球 :金星 :火星 :水星 = 1330:745:65:60:1:0.86:0.15:0.056
# 2、质量:(以地球为1)木星 :土星 :天王星 :海王星 :地球 :金星 :火星 :水星 = 318:95:14.53:17.15:1:0.8:0.11:0.0553
# 3、离太阳从近到远的顺序:水星、金星、地球、火星、木星、土星、天王星、海王星
# =====================================================================
# 以下展示的效果为太阳系真实的距离
# 由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大
sun = Sun(name="太阳", size_scale=0.8e2) # 太阳放大 80 倍,距离保持不变
bodies = [
sun,
Mercury(name="水星", size_scale=4e3), # 水星放大 4000 倍,距离保持不变
Venus(name="金星", size_scale=4e3), # 金星放大 4000 倍,距离保持不变
Earth(name="地球", size_scale=4e3), # 地球放大 4000 倍,距离保持不变
Mars(name="火星", size_scale=4e3), # 火星放大 4000 倍,距离保持不变
Asteroids(name="小行星群", size_scale=3.2e2,
parent=sun), # 小行星群模拟(仅 ursina 模拟器支持)
Jupiter(name="木星", size_scale=0.8e3), # 木星放大 800 倍,距离保持不变
Saturn(name="土星", size_scale=0.8e3), # 土星放大 800 倍,距离保持不变
Uranus(name="天王星", size_scale=0.8e3), # 天王星放大 800 倍,距离保持不变
Neptune(name="海王星", size_scale=1e3), # 海王星放大 1000 倍,距离保持不变
Pluto(name="冥王星", size_scale=10e3), # 冥王星放大 10000 倍,距离保持不变(从太阳系的行星中排除)
]
# 遍历所有天体,
for idx, body in enumerate(bodies):
body.set_ignore_mass(True) # 忽略质量(引力无效)
body.init_velocity = [0, 0, 0] # 初速度为0
# 用天体模拟一个光子
light_body = Body(name='光', mass=0, size_scale=1e4, color=(255, 255, 0),
init_position=[AU / 2, 0, 0],
init_velocity=[0, 0, 299792.458]).set_light_disable(True) # 1 光速=299792.458 千米/秒(km/秒)
bodies.append(light_body)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, 60, position=(0, 2 * AU, -11 * AU),
show_trail=True, show_timer=True,
bg_music="sounds/interstellar.mp3")
# 光到达地球8.3分钟,
# 光到达冥王星平均用时要20000秒,333.3分钟 也就是约5.56小时
from ursina import Text, Ursina, application
import datetime
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
class Timer(Text):
def __init__(self):
# 创建一个文本对象来显示计时器的时间
super().__init__(text='00:00', position=(-0.85, 0.49), font=UrsinaConfig.CN_FONT)
# 用来计时的变量
# self.start_time = time.time()
self.reset()
UrsinaEvent.on_timer_changed_subscription(self.update)
UrsinaEvent.on_reset_subscription(self.reset)
UrsinaEvent.on_pause_subscription(self.pause)
UrsinaEvent.on_start_subscription(self.start)
self.elapsed_time_offset = datetime.timedelta(microseconds=1)
def pause(self):
pass
def start(self):
self.last_time = datetime.datetime.now()
def reset(self):
self.last_time = datetime.datetime.now()
self.elapsed_time = datetime.timedelta(0)
def update(self, evolve_dt=1):
# # 计算当前的时间
# elapsed_time = time.time() - self.start_time
#
# # 将时间转换成“分钟:秒”的形式
# minutes = int(elapsed_time // 60)
# seconds = int(elapsed_time % 60)
# self.text = f'{minutes:02d}:{seconds:02d}'
time_scale = UrsinaConfig.get_app_time_scale()
current_time = datetime.datetime.now()
# 0.653 是对测试太阳系时间的纠正
self.elapsed_time += (current_time - self.last_time) * evolve_dt * time_scale * 0.653
# datetime.timedelta(microseconds=1) 0:00:00.000001
# datetime.timedelta(milliseconds=1) 0:00:00.001000
self.elapsed_time += self.elapsed_time_offset # 按区域取值
self.last_time = current_time
hours, remainder = divmod(self.elapsed_time.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
days = self.elapsed_time.days
self.text = f'{days}天, {hours:02d}:{minutes:02d}:{seconds:02d}'
if __name__ == '__main__':
app = Ursina()
t = Timer()
def update():
t.update()
app.run()
......@@ -182,6 +182,7 @@ class ControlHandler(EventHandler):
if self.ui.on_off_switch.value == self.ui.pause_button_text:
self.ui.on_off_switch.selected_color = color.green
application.paused = True
UrsinaEvent.on_pause()
for c in self.ui.children:
if not c.ignore_paused:
# c.enabled = True
......@@ -189,6 +190,7 @@ class ControlHandler(EventHandler):
else:
self.ui.on_off_switch.selected_color = color.red
application.paused = False
UrsinaEvent.on_start()
for c in self.ui.children:
if not c.ignore_paused:
# c.enabled = True
......
......@@ -31,8 +31,8 @@ class ControlUI(UiPanel):
self.trail_button_text = "○--"
self.slider_body_spin_factor = UiSlider(text='自转速度', min=0.01, max=5, default=1)
self.slider_body_size_factor = UiSlider(text='天体缩放', min=0.1, max=100, step=0.1, default=1)
self.slider_run_speed_factor = UiSlider(text="运行速度", min=0.01, max=80, default=1)
self.slider_body_size_factor = UiSlider(text='天体缩放', min=0.1, max=10, step=0.1, default=1)
self.slider_run_speed_factor = UiSlider(text="运行速度", min=0.01, max=20, default=1)
self.slider_control_speed_factor = UiSlider(text="控制速度", min=0.01, max=10,
step=0.1, default=application.time_scale)
self.slider_trail_length = UiSlider(text="拖尾长度", min=30, max=500, step=10, default=UrsinaConfig.trail_length)
......
......@@ -19,6 +19,7 @@ class UiSlider(Slider):
"""
"""
def __init__(self, text, min=0.01, max=3, step=.01, default=1):
# Text.default_font = 'msyhl.ttc' # 'simsun.ttc'
super().__init__(text=text,
......@@ -32,17 +33,28 @@ class UiSlider(Slider):
ignore_paused=True,
dynamic=True)
# self.label.scale *= 8/10
self.label.font = UrsinaConfig.CN_FONT
self.knob.ignore_paused = True
# self.label.collider = 'box'
# self.label.input = self.input
# self.knob.text_entity.font = ""
# self.knob.text_entity.scale *= 8/10
# self.height *= 8/10
def input(self, key):
if self.hovered:
if key == "double click":
print("Slider->" + key)
self.value = self.default
class SwithButton(ButtonGroup):
"""
"""
def __init__(self, options, default, tooltips=None):
super().__init__(options, min_selection=1, default=default,
selected_color=color.rgba(0.1, 0.6, 0.1, 1.0), ignore_paused=True,
......@@ -63,6 +75,7 @@ class Buttons(ButtonGroup):
"""
"""
def __init__(self, options, default=None, tooltips=None):
min_selection = len(options)
super().__init__(options, min_selection=1, default=default,
......@@ -86,6 +99,7 @@ class UiButton(Button):
"""
"""
def __init__(self, text, on_click):
super(UiButton, self).__init__(text=text, origin=(0, 0), y=2,
on_click=on_click, color=color.rgba(0.0, 0.0, 0.0, 0.5),
......
......@@ -7,6 +7,7 @@
# python_version :3.8
# ==============================================================================
# pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com ursina
from ursina import application
class UrsinaConfig:
......@@ -51,6 +52,11 @@ class UrsinaConfig:
cls.body_size_factor = 1.0
# 天体缩放的因子(不能太大,否则无法容得下大数量级的天体)调整 5e-7 最佳
cls.SCALE_FACTOR = 5e-7
cls.time_scale_offset = 1.0
@classmethod
def get_app_time_scale(cls):
return cls.time_scale_offset * application.time_scale
@property
@classmethod
......
......@@ -20,12 +20,19 @@ class UrsinaEvent:
return
# 重启运行的订阅事件
UrsinaEvent.on_reset_funcs = []
# 暂停运行的订阅事件
UrsinaEvent.on_pause_funcs = []
# 启动运行的订阅事件
UrsinaEvent.on_start_funcs = []
# 搜索天体的订阅事件
UrsinaEvent.on_searching_bodies_funcs = []
# 应用运行的订阅事件
UrsinaEvent.on_application_run_callback = []
#
# 天体大小发生变化的订阅事件
UrsinaEvent.on_body_size_changed_callback = []
# 计时器触发的订阅事件
UrsinaEvent.on_timer_changed_callback = []
@staticmethod
def on_body_size_changed_subscription(fun):
......@@ -36,6 +43,15 @@ class UrsinaEvent:
for f in UrsinaEvent.on_body_size_changed_callback:
f()
@staticmethod
def on_timer_changed_subscription(fun):
UrsinaEvent.on_timer_changed_callback.append(fun)
@staticmethod
def on_timer_changed(evolve_dt):
for f in UrsinaEvent.on_timer_changed_callback:
f(evolve_dt)
@staticmethod
def on_application_run_callback_subscription(fun):
UrsinaEvent.on_application_run_callback.append(fun)
......@@ -53,6 +69,25 @@ class UrsinaEvent:
for f in UrsinaEvent.on_reset_funcs:
f()
@staticmethod
def on_start_subscription(fun):
UrsinaEvent.on_start_funcs.append(fun)
@staticmethod
def on_start():
for f in UrsinaEvent.on_start_funcs:
f()
@staticmethod
def on_pause_subscription(fun):
UrsinaEvent.on_pause_funcs.append(fun)
@staticmethod
def on_pause():
for f in UrsinaEvent.on_pause_funcs:
f()
@staticmethod
def on_application_run():
if len(UrsinaEvent.on_application_run_callback) == 0:
......
......@@ -7,9 +7,7 @@
# python_version :3.8
# ==============================================================================
# pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com ursina
from ursina import Ursina, window, Entity, Grid, Mesh, camera, Text, application, color, mouse, Vec2, Vec3, \
load_texture, held_keys, distance, Audio, scene
from ursina.prefabs.first_person_controller import FirstPersonController
from ursina import Ursina, window, Entity, Grid, camera, application, color, distance, Audio
import itertools
from simulators.ursina.ursina_event import UrsinaEvent
# from simulators.ursina.ursina_ui import UrsinaUI
......@@ -18,16 +16,13 @@ from simulators.ursina.ui.control_handler import ControlHandler
from simulators.ursina.ursina_mesh import create_arrow_line
from simulators.views.ursina_view import UrsinaView
from simulators.ursina.entities.ursina_player import UrsinaPlayer
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.simulator import Simulator
from common.system import System
from common.func import find_file
import time
import datetime
import math
import os
from ursina import EditorCamera, PointLight, SpotLight, AmbientLight, DirectionalLight
from ursina import EditorCamera
from sim_scenes.func import ursina_run
......@@ -141,8 +136,10 @@ class UrsinaSimulator(Simulator):
time_scale = round(pow(max_distance, 1 / 4), 2)
if time_scale < 0.01:
time_scale = 0.01
# camera.scale
# sence
application.time_scale = time_scale
UrsinaConfig.time_scale_offset = 1 / application.time_scale
# UrsinaConfig.auto_scale_factor = 1.0e-9
def on_searching_bodies(self, **kwargs):
......@@ -174,10 +171,19 @@ class UrsinaSimulator(Simulator):
else:
# 配置中,每年、月、天等等有多少秒
evolve_dt = UrsinaConfig.seconds_per * run_speed_factor
UrsinaEvent.on_timer_changed(evolve_dt)
# interval_fator 能让更新天体运行状态(位置、速度)更精确
evolve_dt = evolve_dt * self.interval_fator
super().evolve(evolve_dt)
def create_timer(self):
from simulators.ursina.entities.timer import Timer
# 创建一个文本对象来显示计时器的时间
self.timer = Timer()
return self.timer
def cosmic_background(self, texture='../textures/cosmic2.jpg'):
"""
加入宇宙背景
......@@ -216,6 +222,10 @@ class UrsinaSimulator(Simulator):
if "view_closely" in kwargs:
view_closely = kwargs["view_closely"]
show_timer = False
if "show_timer" in kwargs:
show_timer = kwargs["show_timer"]
if view_closely:
# 近距离查看
if isinstance(view_closely, float):
......@@ -265,8 +275,12 @@ class UrsinaSimulator(Simulator):
if cosmic_bg is not None and os.path.exists(cosmic_bg):
self.cosmic_background(cosmic_bg)
# ui = UrsinaUI()
ctl = ControlUI(ControlHandler(), position=(0.6, 0.5))
if show_timer:
self.create_timer()
EditorCamera(ignore_paused=True)
# 防止打开中文输入法
......@@ -301,7 +315,7 @@ class UrsinaSimulator(Simulator):
if __name__ == '__main__':
from bodies import Sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Moon
from bodies.body import AU
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_HALF_DAY
from common.consts import SECONDS_PER_DAY
"""
3个太阳、1个地球
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册