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

太阳系三体模拟器

上级 72781142
......@@ -9,6 +9,7 @@
import matplotlib.pyplot as plt
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_HALF_DAY
from common.system import System
from simulators.ursina.ursina_event import UrsinaEvent
def mayavi_run(bodies, dt=SECONDS_PER_WEEK,
......@@ -111,6 +112,7 @@ def ursina_run(bodies,
# camera.look_at(entity.position)
def callback_update():
UrsinaEvent.on_application_run()
for ursina_view in simulator.ursina_views:
simulator.check_and_evolve()
if ursina_view.appeared:
......
# -*- coding:utf-8 -*-
# title :ursina天体运行模拟器UI控制
# description :ursina天体运行模拟器UI控制
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from ursina import Ursina, window, Entity, Grid, Mesh, camera, Text, application, color, mouse, Vec2, Vec3, \
load_texture, held_keys, Button, ButtonList, destroy, scene, distance, Sequence, Wait, Func
from ursina.prefabs.first_person_controller import FirstPersonController
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_HALF_DAY, \
SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR
from common.consts import AU
from simulators.ursina.ui.ui_panel import UiPanel
from simulators.ursina.ui_component import UiSlider, SwithButton, UiButton, Buttons
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
from ursina import WindowPanel, InputField, Button, Slider, ButtonGroup, Panel, invoke
from simulators.ursina.ui.event_handler import EventHandler
class ControlHandler(EventHandler):
def handler_input_init(self):
self.settings_handler = Entity(ignore_paused=True)
self.settings_handler.input = self.settings_handler_input
key_info_str = "方位控制[键盘QWEASD]+[鼠标右键],按[空格]更多控制"
key_info = Text(text=key_info_str, font=UrsinaConfig.CN_FONT, position=(-1, 0.5), origin=(-1, 1),
background=True)
def sec_per_time_switch_changed(self):
# ("默认", "天", "周", "月", "年", "十年", "百年")
if self.ui.sec_per_time_switch.value == "天":
UrsinaConfig.seconds_per = SECONDS_PER_DAY
elif self.ui.sec_per_time_switch.value == "周":
UrsinaConfig.seconds_per = SECONDS_PER_WEEK
elif self.ui.sec_per_time_switch.value == "月":
UrsinaConfig.seconds_per = SECONDS_PER_MONTH
elif self.ui.sec_per_time_switch.value == "年":
UrsinaConfig.seconds_per = SECONDS_PER_YEAR
elif self.ui.sec_per_time_switch.value == "十年":
UrsinaConfig.seconds_per = SECONDS_PER_YEAR * 10
elif self.ui.sec_per_time_switch.value == "百年":
UrsinaConfig.seconds_per = SECONDS_PER_YEAR * 100
else:
UrsinaConfig.seconds_per = 0
def on_off_trail_changed(self):
if self.ui.on_off_trail.value == self.ui.trail_button_text:
UrsinaConfig.show_trail = True
else:
UrsinaConfig.show_trail = False
def move_camera_to_entity(self, camera_pos: Vec3, entity_pos: Vec3, _distance: float) -> Vec3:
# 计算摄像机到实体的向量
direction = entity_pos - camera_pos
# 计算当前距离
current_distance = direction.length()
# 如果当前距离已经小于等于要求的距离,则直接返回实体坐标
if current_distance <= _distance:
return camera_pos
# 计算需要移动的距离
_distance = current_distance - _distance
# 根据需要移动的距离计算移动向量
move_vector = direction.normalized() * _distance
# 返回摄像机移动后的坐标
return camera_pos + move_vector
def move_camera_to_entity(self, entity, d):
camera.position = entity.position # - Vec3(0, 0, d) # 设置摄像机位置
camera.world_position = entity.position
def bodies_button_list_click(self, item):
if item is not None:
# TODO: 先找到位置,确定摄像机的位置
try:
d = item.planet.scale_x * 20
self.move_camera_to_entity(item.planet, d)
except Exception as e:
self.ui.show_message(f"{item}飞不见了")
self.bodies_button_list_close()
def bodies_button_list_close(self):
if hasattr(self, "bodies_button_list"):
self.bodies_button_list.enabled = False
destroy(self.bodies_button_list)
def on_searching_bodies_click(self):
results = UrsinaEvent.on_searching_bodies()
if len(results) > 0:
sub_name, bodies = results[0]
if len(bodies) == 0:
self.show_message("天体都飞不见了,请重新运行。")
# button_dict = {"天体都飞不见了,请重新运行。": lambda: self.bodies_button_list_click(None)}
return
# print(results[0])
button_dict = {"[关闭] == 寻找天体 ==": lambda: self.bodies_button_list_click(None)}
camera = scene.camera
for body in bodies:
def callback_action(b=body):
self.bodies_button_list_click(b)
if body.appeared:
distance_to_entity = distance(body.planet, camera)
d = distance_to_entity / UrsinaConfig.SCALE_FACTOR / AU
name = f"{body.name}\t距离:{d:.4f}天文单位"
button_dict[name] = callback_action
else:
if hasattr(self, "bodies_button_list"):
self.bodies_button_list_close()
name = f"{body.name}\t距离太远,找不到了"
button_dict[name] = lambda: self.bodies_button_list_click(None)
if hasattr(self, "bodies_button_list"):
self.bodies_button_list_close()
self.bodies_button_list = ButtonList(button_dict, font=UrsinaConfig.CN_FONT, button_height=1.5)
def on_reset_button_click(self):
UrsinaEvent.on_reset()
def on_buttons_changed(self):
if self.ui.buttons.value == "寻找":
self.on_searching_bodies_click()
elif self.ui.buttons.value == "重启":
self.on_reset_button_click()
def on_off_switch_changed(self):
if self.ui.on_off_switch.value == self.ui.pause_button_text:
self.ui.on_off_switch.selected_color = color.green
application.paused = True
for c in self.ui.children:
if not c.ignore_paused:
# c.enabled = True
c.disabled = False
else:
self.ui.on_off_switch.selected_color = color.red
application.paused = False
for c in self.ui.children:
if not c.ignore_paused:
# c.enabled = True
c.disabled = False
def on_slider_trail_length_changed(self):
UrsinaConfig.trail_length = int(self.ui.slider_trail_length.value)
def on_slider_control_speed_changed(self):
application.time_scale = self.ui.slider_control_speed_factor.value
def on_slider_body_spin_changed(self):
UrsinaConfig.body_spin_factor = self.ui.slider_body_spin_factor.value
def on_slider_body_size_changed(self):
UrsinaConfig.body_size_factor = self.ui.slider_body_size_factor.value
def on_slider_run_speed_changed(self):
UrsinaConfig.run_speed_factor = self.ui.slider_run_speed_factor.value
def settings_handler_input(self, key):
import sys
if key == "escape":
sys.exit()
# print(key)
elif key == 'space':
self.ui.enabled = not self.ui.enabled
elif key == 'left mouse down':
print(key)
elif key == 'y': # 寻找天体
if hasattr(self, "bodies_button_list"):
if self.ui.bodies_button_list.enabled:
self.bodies_button_list_close()
return
self.on_searching_bodies_click()
elif key == 'o': # 重新开始
paused = application.paused
if paused: # 如果是暂停状态,先不暂停,等重新开始后再暂停
application.paused = False
self.on_reset_button_click()
if paused:
def application_paused():
application.paused = True
UrsinaEvent.on_application_run_callback_subscription(application_paused)
elif key == 'i': # 拖尾开关
if self.ui.on_off_trail.value == self.ui.trail_button_text:
self.ui.on_off_trail.value = self.ui.no_trail_button_text
else:
self.ui.on_off_trail.value = self.ui.trail_button_text
self.on_off_trail_changed()
elif key == 'p': # 开始、暂停
if self.ui.on_off_switch.value == self.ui.pause_button_text:
self.ui.on_off_switch.value = self.ui.start_button_text
else:
self.ui.on_off_switch.value = self.ui.pause_button_text
self.on_off_switch_changed()
elif key == '+' or key == "= up":
run_speed_factor = self.ui.slider_run_speed_factor.value + self.ui.slider_run_speed_factor.step * 50
if run_speed_factor > self.ui.slider_run_speed_factor.max:
run_speed_factor = self.ui.slider_run_speed_factor.max
self.ui.slider_run_speed_factor.value = run_speed_factor
self.ui.slider_run_speed_factor.knob.drop()
elif key == '-' or key == "- up":
run_speed_factor = self.ui.slider_run_speed_factor.value - self.ui.slider_run_speed_factor.step * 50
if run_speed_factor < self.ui.slider_run_speed_factor.min:
run_speed_factor = self.ui.slider_run_speed_factor.min
self.ui.slider_run_speed_factor.value = run_speed_factor
self.ui.slider_run_speed_factor.knob.drop()
# -*- coding:utf-8 -*-
# title :ursina天体运行模拟器UI控制
# description :ursina天体运行模拟器UI控制
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from ursina import Ursina, window, Entity, Grid, Mesh, camera, Text, application, color, mouse, Vec2, Vec3, \
load_texture, held_keys, Button, ButtonList, destroy, scene, distance, Sequence, Wait, Func
from ursina.prefabs.first_person_controller import FirstPersonController
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_HALF_DAY, \
SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR
from common.consts import AU
from simulators.ursina.ui.control_handler import ControlHandler
from simulators.ursina.ui.ui_panel import UiPanel
from simulators.ursina.ui_component import UiSlider, SwithButton, UiButton, Buttons
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
from ursina import WindowPanel, InputField, Button, Slider, ButtonGroup, Panel, invoke
class ControlUI(UiPanel):
def component_init(self):
self.start_button_text = "●" # 》●▲○◎
self.pause_button_text = "〓" # 〓 || ‖
self.no_trail_button_text = "○ "
self.trail_button_text = "○--"
self.slider_body_spin_factor = UiSlider(text='自转速度', min=0.01, max=30, 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_control_speed_factor = UiSlider(text="控制速度", min=0.01, max=20, default=application.time_scale)
self.slider_trail_length = UiSlider(text="拖尾长度", min=30, max=500, step=10, default=UrsinaConfig.trail_length)
self.on_off_switch = SwithButton((self.pause_button_text,
self.start_button_text),
default=self.start_button_text,
tooltips=('暂停(P)', '运行(P)'))
self.on_off_switch.selected_color = color.red
self.sec_per_time_switch = SwithButton(("默认", "天", "周", "月", "年", "十年", "百年"),
default="默认",
tooltips=("系统默认", "每秒相当于1天", "每秒相当于1周",
"每秒相当于1个月",
"每秒相当于1年", "每秒相当于十年", "每秒相当于1百年"))
self.on_off_trail = SwithButton((self.no_trail_button_text, self.trail_button_text),
default=self.no_trail_button_text,
tooltips=('天体运行无轨迹', '天体运行有拖尾轨迹'))
self.point_button = UiButton(text='寻找天体(Y)', on_click=None)
self.reset_button = UiButton(text='重新开始(O)', on_click=None)
content = (
# InputField(name='name_field'),
# Button(text='Submit', color=color.azure),
self.point_button,
self.reset_button,
# self.buttons,
self.sec_per_time_switch,
self.on_off_switch,
self.on_off_trail,
self.slider_trail_length,
self.slider_body_size_factor,
self.slider_body_spin_factor,
self.slider_run_speed_factor,
self.slider_control_speed_factor
)
return content
def after_component_init(self):
self.sec_per_time_switch.x = -0.4
self.on_off_switch.x = 0.2
self.on_off_trail.x = 0.2 # -0.4
def event_handler_init(self):
self.slider_body_size_factor.on_value_changed = self.handler.on_slider_body_size_changed
self.slider_body_spin_factor.on_value_changed = self.handler.on_slider_body_spin_changed
self.slider_run_speed_factor.on_value_changed = self.handler.on_slider_run_speed_changed
self.slider_control_speed_factor.on_value_changed = self.handler.on_slider_control_speed_changed
self.slider_trail_length.on_value_changed = self.handler.on_slider_trail_length_changed
self.on_off_trail.on_value_changed = self.handler.on_off_trail_changed
self.on_off_switch.on_value_changed = self.handler.on_off_switch_changed
self.point_button.on_click = self.handler.on_searching_bodies_click
self.reset_button.on_click = self.handler.on_reset_button_click
self.sec_per_time_switch.on_value_changed = self.handler.sec_per_time_switch_changed
if __name__ == '__main__':
app = Ursina()
ctl = ControlUI(ControlHandler(), position=(0.6, 0.5), enabled=True)
app.run()
# -*- coding:utf-8 -*-
# title :ursina天体运行模拟器UI控制
# description :ursina天体运行模拟器UI控制
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
class EventHandler:
def __init__(self):
self.ui = None
self.handler_input_init()
def handler_input_init(self):
pass
# -*- coding:utf-8 -*-
# title :ursina天体运行模拟器UI控制
# description :ursina天体运行模拟器UI控制
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from ursina import Ursina, window, Entity, Grid, Mesh, camera, Text, application, color, mouse, Vec2, Vec3, \
load_texture, held_keys, Button, ButtonList, destroy, scene, distance, Sequence, Wait, Func
from ursina.prefabs.first_person_controller import FirstPersonController
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_HALF_DAY, \
SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR
from common.consts import AU
from simulators.ursina.ui_component import UiSlider, SwithButton, UiButton, Buttons
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
from ursina import WindowPanel, InputField, Button, Slider, ButtonGroup, Panel, invoke
from simulators.ursina.ui.event_handler import EventHandler
class UiPanel(WindowPanel):
def __init__(self, handler: EventHandler, position=(0, 0), enabled=False, title=''):
self.components = self.component_init()
self.handler = handler
self.handler.ui = self
self.event_handler_init()
super().__init__(title=title, content=self.components, ignore_paused=True, color=color.rgba(0.0, 0.0, 0.0, 0.5))
self.y = position[1] # wp.panel.scale_y / 2 * wp.scale_y # center the window panel
self.x = position[0] # wp.scale_x + 0.1
self.enabled = enabled
self.after_component_init()
def after_component_init(self):
pass
def component_init(self):
pass
def event_handler_init(self):
pass
def show_message(self, message, close_time=3):
"""
创建消息框
:param message: 消息内容
:param close_time: 定义关闭时间
:return:
"""
# 创建消息框
message_box = Text(text=message, font=UrsinaConfig.CN_FONT, background=True, origin=(0, 0), y=.25)
# 定义关闭函数
def close_message():
destroy(message_box)
s = Sequence(
Wait(close_time),
Func(close_message)
)
s.start()
......@@ -52,6 +52,26 @@ class SwithButton(ButtonGroup):
self.x = -0.5
class Buttons(ButtonGroup):
def __init__(self, options, default=None, tooltips=None):
min_selection = len(options)
super().__init__(options, min_selection=1, default=default,
color=color.rgba(0.1, 0.6, 0.1, 1.0), ignore_paused=True,
selected_color=color.rgba(0.0, 0.0, 0.0, 0.5))
# self.label.scale = 0.8
# self.label.font = UrsinaConfig.CN_FONT\
for i, button in enumerate(self.buttons):
button.text_entity.font = UrsinaConfig.CN_FONT
# button.scale_x = 2
if tooltips is not None:
if len(tooltips) > i:
tooltip = Tooltip(tooltips[i])
tooltip.font = UrsinaConfig.CN_FONT
button.tooltip = tooltip
self.x = -0.5
class UiButton(Button):
def __init__(self, text, on_click):
super(UiButton, self).__init__(text=text, origin=(0, 0), y=2,
......
......@@ -20,6 +20,11 @@ class UrsinaEvent:
return
UrsinaEvent.on_reset_funcs = []
UrsinaEvent.on_searching_bodies_funcs = []
UrsinaEvent.on_application_run_callback = []
@staticmethod
def on_application_run_callback_subscription(fun):
UrsinaEvent.on_application_run_callback.append(fun)
@staticmethod
def on_searching_bodies_subscription(subscription_name, fun):
......@@ -34,6 +39,14 @@ class UrsinaEvent:
for f in UrsinaEvent.on_reset_funcs:
f()
@staticmethod
def on_application_run():
if len(UrsinaEvent.on_application_run_callback) == 0:
return
for f in UrsinaEvent.on_application_run_callback:
f()
UrsinaEvent.on_application_run_callback.clear()
@staticmethod
def on_searching_bodies(**kwargs):
results = []
......
......@@ -12,7 +12,7 @@ from ursina.prefabs.first_person_controller import FirstPersonController
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_HALF_DAY, \
SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR
from common.consts import AU
from simulators.ursina.ui_component import UiSlider, SwithButton, UiButton
from simulators.ursina.ui_component import UiSlider, SwithButton, UiButton, Buttons
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
from ursina import WindowPanel, InputField, Button, Slider, ButtonGroup, Panel, invoke
......@@ -58,6 +58,9 @@ class UrsinaUI:
tooltips=('天体运行无轨迹', '天体运行有拖尾轨迹'))
self.on_off_trail.on_value_changed = self.on_off_trail_changed
# self.buttons = Buttons(("寻找", "重启"), tooltips=("寻找天体(Y)", "重新开始(O)"))
self.point_button = UiButton(text='寻找天体(Y)', on_click=self.on_searching_bodies_click)
self.reset_button = UiButton(text='重新开始(O)', on_click=self.on_reset_button_click)
......@@ -85,6 +88,7 @@ class UrsinaUI:
# Button(text='Submit', color=color.azure),
self.point_button,
self.reset_button,
# self.buttons,
self.sec_per_time_switch,
self.on_off_switch,
self.on_off_trail,
......@@ -275,6 +279,12 @@ class UrsinaUI:
def on_reset_button_click(self):
UrsinaEvent.on_reset()
def on_buttons_changed(self):
if self.buttons.value == "寻找":
self.on_searching_bodies_click()
elif self.buttons.value == "重启":
self.on_reset_button_click()
def on_off_switch_changed(self):
if self.on_off_switch.value == self.pause_button_text:
self.on_off_switch.selected_color = color.green
......
......@@ -12,7 +12,10 @@ from ursina import Ursina, window, Entity, Grid, Mesh, camera, Text, application
from ursina.prefabs.first_person_controller import FirstPersonController
from simulators.ursina.ursina_event import UrsinaEvent
from simulators.ursina.ursina_ui import UrsinaUI
# from simulators.ursina.ursina_ui import UrsinaUI
from simulators.ursina.ui.control_ui import ControlUI
from simulators.ursina.ui.control_handler import ControlHandler
from simulators.views.ursina_view import UrsinaView, UrsinaPlayer
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.simulator import Simulator
......@@ -236,7 +239,9 @@ class UrsinaSimulator(Simulator):
if cosmic_bg is not None and os.path.exists(cosmic_bg):
self.cosmic_background(cosmic_bg)
ui = UrsinaUI()
# ui = UrsinaUI()
ctl = ControlUI(ControlHandler(), position=(0.6, 0.5))
EditorCamera(ignore_paused=True)
# 防止打开中文输入法
# self.switch_to_english_input_method()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册