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

太阳系三体模拟器

上级 1d01c0a6
...@@ -116,16 +116,27 @@ class ControlHandler(EventHandler): ...@@ -116,16 +116,27 @@ class ControlHandler(EventHandler):
if hasattr(self, "bodies_button_list"): if hasattr(self, "bodies_button_list"):
self.bodies_button_list_close() self.bodies_button_list_close()
self.bodies_button_list = ButtonList(button_dict, font=UrsinaConfig.CN_FONT, button_height=1.5) self.bodies_button_list = ButtonList(button_dict,
font=UrsinaConfig.CN_FONT,
button_height=1.5,
ignore_paused=True)
def on_reset_button_click(self): def on_reset_button_click(self):
paused = application.paused
if paused: # 如果是暂停状态,先不暂停,等重新开始后再暂停
application.paused = False
UrsinaEvent.on_reset() UrsinaEvent.on_reset()
if paused:
def application_paused():
application.paused = True
def on_buttons_changed(self): UrsinaEvent.on_application_run_callback_subscription(application_paused)
if self.ui.buttons.value == "寻找":
self.on_searching_bodies_click() # def on_buttons_changed(self):
elif self.ui.buttons.value == "重启": # if self.ui.buttons.value == "寻找":
self.on_reset_button_click() # self.on_searching_bodies_click()
# elif self.ui.buttons.value == "重启":
# self.on_reset_button_click()
def on_off_switch_changed(self): def on_off_switch_changed(self):
if self.ui.on_off_switch.value == self.ui.pause_button_text: if self.ui.on_off_switch.value == self.ui.pause_button_text:
...@@ -181,6 +192,7 @@ class ControlHandler(EventHandler): ...@@ -181,6 +192,7 @@ class ControlHandler(EventHandler):
if paused: if paused:
def application_paused(): def application_paused():
application.paused = True application.paused = True
UrsinaEvent.on_application_run_callback_subscription(application_paused) UrsinaEvent.on_application_run_callback_subscription(application_paused)
elif key == 'i': # 拖尾开关 elif key == 'i': # 拖尾开关
......
# -*- 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
class UrsinaUI:
def ui_component_init(self):
self.start_button_text = "●" # 》●▲○◎
self.pause_button_text = "〓" # 〓 || ‖
self.no_trail_button_text = "○ "
self.trail_button_text = "○--"
# application.time_scale = 0.5
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.slider_body_size_factor.on_value_changed = self.on_slider_body_size_changed
self.slider_body_spin_factor.on_value_changed = self.on_slider_body_spin_changed
self.slider_run_speed_factor.on_value_changed = self.on_slider_run_speed_changed
self.slider_control_speed_factor.on_value_changed = self.on_slider_control_speed_changed
self.slider_trail_length.on_value_changed = self.on_slider_trail_length_changed
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.sec_per_time_switch.on_value_changed = self.sec_per_time_switch_changed
self.on_off_trail = SwithButton((self.no_trail_button_text, self.trail_button_text),
default=self.no_trail_button_text,
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)
# button1 = Button(text='Button 1', scale=(0.1, 0.1), position=(-0.1, 0))
# button2 = Button(text='Button 2', scale=(0.1, 0.1), position=(0.1, 0))
# btn_settings = UiButton(text='操作设置', on_click=self.on_point_button_click)
# btn_settings.position = window.top_left
# btn_settings.y = 0.5
# # btn_settings.scale = (0.1,0.1)
# # btn_settings.y = 0
# btn_settings.scale = (.25, .025),
# btn_settings.origin = (-.5, .5),
# btn_settings.pressed_scale = 1,
# if btn_settings.text_entity:
# btn_settings.text_entity.x = .05
# btn_settings.text_entity.origin = (-.5, 0)
# btn_settings.text_entity.scale *= .8
self.on_off_switch.on_value_changed = self.on_off_switch_changed
wp = WindowPanel(
title='',
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
), ignore_paused=True, color=color.rgba(0.0, 0.0, 0.0, 0.5) # , popup=True
)
self.sec_per_time_switch.x = -0.4
self.on_off_switch.x = 0.2
self.on_off_trail.x = 0.2 # -0.4
wp.y = 0.5 # wp.panel.scale_y / 2 * wp.scale_y # center the window panel
wp.x = 0.6 # wp.scale_x + 0.1
# wp.x = 0#wp.panel.scale_x / 2 * wp.scale_x
self.wp = wp
self.wp.enabled = False
def __init__(self):
self.ui_component_init()
self.settings_handler = Entity(ignore_paused=True)
# 加载中文字体文件
# text_time_scale = "1"
# self.text_time_scale_info = None
self.settings_handler.input = self.settings_handler_input
# self.show_text_time_scale_info()
# Text('方位控制: Q W E A S D + 鼠标右键', font='msyhl.ttc'),
key_info_str = "方位控制[键盘QWEASD]+[鼠标右键],按[空格]更多控制"
key_info = Text(text=key_info_str, font=UrsinaConfig.CN_FONT, position=(-1, 0.5), origin=(-1, 1),
background=True)
# # self.show_button()
# slider_text = Text(text='自转速度', scale=1, position=(-0.6, 0.3))
# slider = Slider(scale=0.5, position=(-0.6, 0), min=0, max=10, step=1, text=slider_text)
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()
# # 使用 time 模块来实现定时关闭
# invoke(close_message, delay=close_time)
def sec_per_time_switch_changed(self):
# ("默认", "天", "周", "月", "年", "十年", "百年")
if self.sec_per_time_switch.value == "天":
UrsinaConfig.seconds_per = SECONDS_PER_DAY
elif self.sec_per_time_switch.value == "周":
UrsinaConfig.seconds_per = SECONDS_PER_WEEK
elif self.sec_per_time_switch.value == "月":
UrsinaConfig.seconds_per = SECONDS_PER_MONTH
elif self.sec_per_time_switch.value == "年":
UrsinaConfig.seconds_per = SECONDS_PER_YEAR
elif self.sec_per_time_switch.value == "十年":
UrsinaConfig.seconds_per = SECONDS_PER_YEAR * 10
elif self.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.on_off_trail.value == self.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):
import math
# print("before",camera.position, entity.position)
camera.position = entity.position # - Vec3(0, 0, d) # 设置摄像机位置
camera.world_position = entity.position
# camera.rotation = (0, 0, 0) # 重置摄像机旋转角度
# print("after",camera.position,entity.position)
# # 获取相机和实体之间的向量
# target_vector = entity.position - camera.position
# target_vector.y = 0 # 假设实体在 x-z 平面上,将 y 坐标设为 0
#
# # 计算旋转角度
# angle = math.degrees(math.atan2(target_vector.z, target_vector.x))
# camera.rotation_y = angle # 旋转相机
# camera.look_at(entity.position) # 对准指定实体
def bodies_button_list_click(self, item):
if item is not None:
# TODO: 先找到位置,确定摄像机的位置
# print("select->", item)
# UrsinaConfig.SCALE_FACTOR
# import copy
# camera_rotation = copy.deepcopy(camera.rotation)
try:
d = item.planet.scale_x * 20
self.move_camera_to_entity(item.planet, d)
except Exception as e:
self.show_message(f"{item}飞不见了")
# d = distance(camera.position, item.planet.position)
# camera.look_at(item.planet)
# if d > 1.5 * x:
# move_to = self.move_camera_to_entity(camera.position, item.planet.position, x)
# camera.position = move_to
# camera_rotation = copy.deepcopy(camera.rotation)
# camera.rotation = (camera_rotation[0], camera_rotation[1], 0)
# camera.forward = (1, 0, 0) # 设置相机的方向向量为x轴方向
self.bodies_button_list_close()
# my_entity = Entity(model='cube', color=color.red, position=(0, 1, 5))
#
# # 获取当前摄像机
# camera = scene.camera
#
# # 计算 Entity 和摄像机之间的距离
# distance_to_entity = distance(my_entity, camera)
#
# print('距离:', distance_to_entity)
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)
# self.bodies_button_list.input = self.bodies_button_list_input
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
application.paused = True
for c in self.wp.children:
if not c.ignore_paused:
# c.enabled = True
c.disabled = False
else:
self.on_off_switch.selected_color = color.red
application.paused = False
for c in self.wp.children:
if not c.ignore_paused:
# c.enabled = True
c.disabled = False
def on_slider_trail_length_changed(self):
UrsinaConfig.trail_length = int(self.slider_trail_length.value)
def on_slider_control_speed_changed(self):
application.time_scale = self.slider_control_speed_factor.value
def on_slider_body_spin_changed(self):
UrsinaConfig.body_spin_factor = self.slider_body_spin_factor.value
def on_slider_body_size_changed(self):
UrsinaConfig.body_size_factor = self.slider_body_size_factor.value
def on_slider_run_speed_changed(self):
UrsinaConfig.run_speed_factor = self.slider_run_speed_factor.value
# def show_text_time_scale_info(self):
# if self.text_time_scale_info is not None:
# self.text_time_scale_info.disable()
# text_time_scale = "控制倍率:" + str(application.time_scale).ljust(4, " ")
# text_time_scale_info = Text(text=text_time_scale, position=(-0.8, 0.5), origin=(-1, 1), background=True)
# def show_button(self):
# b = Button(scale=(0, .25), text='zzz')
# if key == "escape":
# if mouse.locked:
# self.on_disable()
# else:
# sys.exit()
# 按空格键则暂停
def settings_handler_input(self, key):
import sys
if key == "escape":
sys.exit()
# print(key)
elif key == 'space':
self.wp.enabled = not self.wp.enabled
elif key == 'left mouse down':
print(key)
elif key == 'y': # 寻找天体
if hasattr(self, "bodies_button_list"):
if self.bodies_button_list.enabled:
self.bodies_button_list_close()
return
self.on_searching_bodies_click()
elif key == 'o': # 重新开始
self.on_reset_button_click()
elif key == 'i': # 拖尾开关
if self.on_off_trail.value == self.trail_button_text:
self.on_off_trail.value = self.no_trail_button_text
else:
self.on_off_trail.value = self.trail_button_text
self.on_off_trail_changed()
elif key == 'p': # 开始、暂停
if self.on_off_switch.value == self.pause_button_text:
self.on_off_switch.value = self.start_button_text
else:
self.on_off_switch.value = self.pause_button_text
self.on_off_switch_changed()
elif key == '+' or key == "= up":
run_speed_factor = self.slider_run_speed_factor.value + self.slider_run_speed_factor.step * 50
if run_speed_factor > self.slider_run_speed_factor.max:
run_speed_factor = self.slider_run_speed_factor.max
self.slider_run_speed_factor.value = run_speed_factor
self.slider_run_speed_factor.knob.drop()
elif key == '-' or key == "- up":
run_speed_factor = self.slider_run_speed_factor.value - self.slider_run_speed_factor.step * 50
if run_speed_factor < self.slider_run_speed_factor.min:
run_speed_factor = self.slider_run_speed_factor.min
self.slider_run_speed_factor.value = run_speed_factor
self.slider_run_speed_factor.knob.drop()
# UrsinaConfig.run_speed_factor *= 2
# application.paused = not application.paused # Pause/unpause the game.
# elif key == 'tab':
# # application.time_scale 属性控制游戏时间流逝的速度。
# # 具体来说,它是一个浮点数,用于调整游戏时间流逝速度的比例,其默认值为 1.0,表示正常速度。
# # 当你将它设置为小于 1.0 的值时,游戏时间会变慢,而设置为大于 1.0 的值时,游戏时间则会变快。
# for idx, time_scale in enumerate(time_scales):
# if float(application.time_scale) == time_scale:
# if idx < len(time_scales) - 1:
# application.time_scale = time_scales[idx + 1]
# break
# else:
# application.time_scale = time_scales[0]
# elif key == '+':
# UrsinaConfig.run_speed_factor *= 2
# elif key == "= up":
# UrsinaConfig.body_spin_factor *= 2
# # if application.time_scale in time_scales:
# # idx = time_scales.index(application.time_scale)
# # if idx < len(time_scales) - 1:
# # application.time_scale = time_scales[idx + 1]
# elif key == '-':
# UrsinaConfig.run_speed_factor *= 0.5
# elif key == "- up":
# UrsinaConfig.body_spin_factor *= 0.5
# # if application.time_scale in time_scales:
# # idx = time_scales.index(application.time_scale)
# # if idx > 0:
# # application.time_scale = time_scales[idx - 1]
#
# self.show_text_time_scale_info()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册