ursina_ui.py 16.1 KB
Newer Older
三月三net's avatar
三月三net 已提交
1 2 3 4 5 6 7 8 9
# -*- 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, \
三月三net's avatar
三月三net 已提交
10
    load_texture, held_keys, Button, ButtonList, destroy, scene, distance, Sequence, Wait, Func
三月三net's avatar
三月三net 已提交
11
from ursina.prefabs.first_person_controller import FirstPersonController
三月三net's avatar
三月三net 已提交
12 13
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_HALF_DAY, \
    SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR
三月三net's avatar
三月三net 已提交
14
from common.consts import AU
三月三net's avatar
三月三net 已提交
15
from simulators.ursina.ui_component import UiSlider, SwithButton, UiButton
三月三net's avatar
三月三net 已提交
16 17
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
三月三net's avatar
三月三net 已提交
18
from ursina import WindowPanel, InputField, Button, Slider, ButtonGroup, Panel, invoke
三月三net's avatar
三月三net 已提交
19 20 21 22


class UrsinaUI:

三月三net's avatar
三月三net 已提交
23
    def ui_component_init(self):
三月三net's avatar
三月三net 已提交
24

三月三net's avatar
三月三net 已提交
25 26 27 28
        self.start_button_text = "●"  # 》●▲○◎
        self.pause_button_text = "〓"  # 〓 || ‖
        self.no_trail_button_text = "○ "
        self.trail_button_text = "○--"
三月三net's avatar
三月三net 已提交
29

三月三net's avatar
三月三net 已提交
30
        application.time_scale = 0.5
三月三net's avatar
三月三net 已提交
31
        self.slider_body_spin_factor = UiSlider(text='自转速度', min=0.01, max=30, default=1)
三月三net's avatar
三月三net 已提交
32
        self.slider_body_size_factor = UiSlider(text='天体缩放', min=0.01, max=10, default=1)
三月三net's avatar
三月三net 已提交
33 34 35
        self.slider_run_speed_factor = UiSlider(text="运行速度", min=0.01, max=800, default=1)
        self.slider_control_speed_factor = UiSlider(text="控制速度", min=0.01, max=30, default=application.time_scale)
        self.slider_trail_length = UiSlider(text="拖尾长度", min=30, max=500, default=UrsinaConfig.trail_length)
三月三net's avatar
三月三net 已提交
36

三月三net's avatar
三月三net 已提交
37
        self.slider_body_size_factor.on_value_changed = self.on_slider_body_size_changed
三月三net's avatar
三月三net 已提交
38 39 40
        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
三月三net's avatar
三月三net 已提交
41 42
        self.slider_trail_length.on_value_changed = self.on_slider_trail_length_changed

三月三net's avatar
三月三net 已提交
43
        self.on_off_switch = SwithButton((self.pause_button_text,
三月三net's avatar
三月三net 已提交
44 45 46
                                          self.start_button_text),
                                         default=self.start_button_text,
                                         tooltips=('暂停', '运行'))
三月三net's avatar
三月三net 已提交
47
        self.on_off_switch.selected_color = color.red
三月三net's avatar
三月三net 已提交
48

三月三net's avatar
三月三net 已提交
49 50 51 52 53
        self.sec_per_time_switch = SwithButton(("默认", "天", "周", "月", "年", "十年", "百年"),
                                               default="默认",
                                               tooltips=("系统默认", "每秒相当于1天", "每秒相当于1周",
                                                         "每秒相当于1个月",
                                                         "每秒相当于1年", "每秒相当于十年", "每秒相当于1百年"))
三月三net's avatar
三月三net 已提交
54
        self.sec_per_time_switch.on_value_changed = self.sec_per_time_switch_changed
三月三net's avatar
三月三net 已提交
55

三月三net's avatar
三月三net 已提交
56 57 58
        self.on_off_trail = SwithButton((self.no_trail_button_text, self.trail_button_text),
                                        default=self.no_trail_button_text,
                                        tooltips=('天体运行无轨迹', '天体运行有拖尾轨迹'))
三月三net's avatar
三月三net 已提交
59 60
        self.on_off_trail.on_value_changed = self.on_off_trail_changed

三月三net's avatar
三月三net 已提交
61
        self.point_button = UiButton(text='寻找', on_click=self.on_searching_bodies_click)
三月三net's avatar
三月三net 已提交
62
        self.reset_button = UiButton(text='重置', on_click=self.on_reset_button_click)
三月三net's avatar
三月三net 已提交
63

三月三net's avatar
三月三net 已提交
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
        # 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
三月三net's avatar
三月三net 已提交
79 80

        self.on_off_switch.on_value_changed = self.on_off_switch_changed
三月三net's avatar
三月三net 已提交
81 82 83
        wp = WindowPanel(
            title='',
            content=(
三月三net's avatar
三月三net 已提交
84
                Text('方位控制: Q W E A S D + 鼠标右键', font='msyhl.ttc'),
三月三net's avatar
三月三net 已提交
85 86 87 88
                # InputField(name='name_field'),
                # Button(text='Submit', color=color.azure),
                self.point_button,
                self.reset_button,
三月三net's avatar
三月三net 已提交
89
                self.sec_per_time_switch,
三月三net's avatar
三月三net 已提交
90
                self.on_off_switch,
三月三net's avatar
三月三net 已提交
91 92
                self.on_off_trail,
                self.slider_trail_length,
三月三net's avatar
三月三net 已提交
93
                self.slider_body_size_factor,
三月三net's avatar
三月三net 已提交
94 95 96 97
                self.slider_body_spin_factor,
                self.slider_run_speed_factor,
                self.slider_control_speed_factor

三月三net's avatar
三月三net 已提交
98
            ), ignore_paused=True, color=color.rgba(0.0, 0.0, 0.0, 0.5)  # , popup=True
三月三net's avatar
三月三net 已提交
99
        )
三月三net's avatar
三月三net 已提交
100 101 102
        self.sec_per_time_switch.x = -0.4
        self.on_off_switch.x = 0.2
        self.on_off_trail.x = -0.4
三月三net's avatar
三月三net 已提交
103
        wp.y = 0.5  # wp.panel.scale_y / 2 * wp.scale_y  # center the window panel
三月三net's avatar
三月三net 已提交
104
        wp.x = 0.6  # wp.scale_x + 0.1
三月三net's avatar
三月三net 已提交
105
        # wp.x = 0#wp.panel.scale_x / 2 * wp.scale_x
三月三net's avatar
三月三net 已提交
106
        self.wp = wp
三月三net's avatar
三月三net 已提交
107
        self.wp.enabled = False
三月三net's avatar
三月三net 已提交
108

三月三net's avatar
三月三net 已提交
109 110 111
    def __init__(self):
        self.ui_component_init()

三月三net's avatar
三月三net 已提交
112
        self.settings_handler = Entity(ignore_paused=True)
三月三net's avatar
三月三net 已提交
113 114 115 116
        # 加载中文字体文件

        # text_time_scale = "1"
        # self.text_time_scale_info = None
三月三net's avatar
三月三net 已提交
117
        self.settings_handler.input = self.settings_handler_input
三月三net's avatar
三月三net 已提交
118
        # self.show_text_time_scale_info()
三月三net's avatar
三月三net 已提交
119 120 121
        key_info_str = "按[空格]设置"
        key_info = Text(text=key_info_str, font=UrsinaConfig.CN_FONT, position=(-0.5, 0.5), origin=(-1, 1),
                        background=True)
三月三net's avatar
三月三net 已提交
122 123 124 125
        # # 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)

三月三net's avatar
三月三net 已提交
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
    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(
三月三net's avatar
三月三net 已提交
141
            Wait(close_time),
三月三net's avatar
三月三net 已提交
142 143 144 145 146 147
            Func(close_message)
        )
        s.start()
        # # 使用 time 模块来实现定时关闭
        # invoke(close_message, delay=close_time)

三月三net's avatar
三月三net 已提交
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
    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

三月三net's avatar
三月三net 已提交
165
    def on_off_trail_changed(self):
三月三net's avatar
三月三net 已提交
166
        if self.on_off_trail.value == self.trail_button_text:
三月三net's avatar
三月三net 已提交
167 168 169 170
            UrsinaConfig.show_trail = True
        else:
            UrsinaConfig.show_trail = False

三月三net's avatar
三月三net 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
    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

三月三net's avatar
三月三net 已提交
186
    def move_camera_to_entity(self, entity, d):
三月三net's avatar
三月三net 已提交
187 188
        import math
        # print("before",camera.position, entity.position)
三月三net's avatar
三月三net 已提交
189
        camera.position = entity.position  # - Vec3(0, 0, d)  # 设置摄像机位置
三月三net's avatar
三月三net 已提交
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
        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)  # 对准指定实体

三月三net's avatar
三月三net 已提交
205
    def bodies_button_list_click(self, item):
三月三net's avatar
三月三net 已提交
206
        if item is not None:
三月三net's avatar
三月三net 已提交
207 208 209
            # TODO: 先找到位置,确定摄像机的位置
            # print("select->", item)
            # UrsinaConfig.SCALE_FACTOR
三月三net's avatar
三月三net 已提交
210 211
            # import copy
            # camera_rotation = copy.deepcopy(camera.rotation)
三月三net's avatar
三月三net 已提交
212 213 214 215 216
            try:
                d = item.planet.scale_x * 20
                self.move_camera_to_entity(item.planet, d)
            except Exception as e:
                self.show_message(f"{item}飞不见了")
三月三net's avatar
三月三net 已提交
217 218 219 220 221 222 223 224
            # 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)
三月三net's avatar
三月三net 已提交
225
            # camera.forward = (1, 0, 0)  # 设置相机的方向向量为x轴方向
三月三net's avatar
三月三net 已提交
226 227 228

        destroy(self.bodies_button_list)

三月三net's avatar
三月三net 已提交
229 230 231 232 233 234 235 236 237 238
    # 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)

三月三net's avatar
三月三net 已提交
239 240 241 242
    def on_searching_bodies_click(self):
        results = UrsinaEvent.on_searching_bodies()
        if len(results) > 0:
            sub_name, bodies = results[0]
三月三net's avatar
三月三net 已提交
243 244 245 246
            if len(bodies) == 0:
                self.show_message("天体都飞不见了,请重新运行。")
                # button_dict = {"天体都飞不见了,请重新运行。": lambda: self.bodies_button_list_click(None)}
                return
三月三net's avatar
三月三net 已提交
247
            # print(results[0])
三月三net's avatar
三月三net 已提交
248
            button_dict = {"[关闭]": lambda: self.bodies_button_list_click(None)}
三月三net's avatar
三月三net 已提交
249
            camera = scene.camera
三月三net's avatar
三月三net 已提交
250 251 252 253
            for body in bodies:
                def callback_action(b=body):
                    self.bodies_button_list_click(b)

三月三net's avatar
三月三net 已提交
254 255 256 257 258 259
                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:
三月三net's avatar
三月三net 已提交
260 261
                    if hasattr(self, "bodies_button_list"):
                        destroy(self.bodies_button_list)
三月三net's avatar
三月三net 已提交
262 263
                    name = f"{body.name}\t距离太远,找不到了"
                    button_dict[name] = lambda: self.bodies_button_list_click(None)
三月三net's avatar
三月三net 已提交
264

三月三net's avatar
三月三net 已提交
265 266
            if hasattr(self, "bodies_button_list"):
                destroy(self.bodies_button_list)
三月三net's avatar
三月三net 已提交
267

三月三net's avatar
三月三net 已提交
268 269
            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
三月三net's avatar
三月三net 已提交
270 271 272 273 274

    def on_reset_button_click(self):
        UrsinaEvent.on_reset()

    def on_off_switch_changed(self):
三月三net's avatar
三月三net 已提交
275
        if self.on_off_switch.value == self.pause_button_text:
三月三net's avatar
三月三net 已提交
276 277
            self.on_off_switch.selected_color = color.green
            application.paused = True
三月三net's avatar
三月三net 已提交
278 279 280 281
            for c in self.wp.children:
                if not c.ignore_paused:
                    # c.enabled = True
                    c.disabled = False
三月三net's avatar
三月三net 已提交
282 283 284
        else:
            self.on_off_switch.selected_color = color.red
            application.paused = False
三月三net's avatar
三月三net 已提交
285 286 287 288
            for c in self.wp.children:
                if not c.ignore_paused:
                    # c.enabled = True
                    c.disabled = False
三月三net's avatar
三月三net 已提交
289

三月三net's avatar
三月三net 已提交
290 291 292
    def on_slider_trail_length_changed(self):
        UrsinaConfig.trail_length = int(self.slider_trail_length.value)

三月三net's avatar
三月三net 已提交
293 294 295 296 297 298
    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

三月三net's avatar
三月三net 已提交
299 300 301
    def on_slider_body_size_changed(self):
        UrsinaConfig.body_size_factor = self.slider_body_size_factor.value

三月三net's avatar
三月三net 已提交
302 303 304
    def on_slider_run_speed_changed(self):
        UrsinaConfig.run_speed_factor = self.slider_run_speed_factor.value

三月三net's avatar
三月三net 已提交
305 306 307 308 309
    # 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)
三月三net's avatar
三月三net 已提交
310

三月三net's avatar
三月三net 已提交
311 312
    # def show_button(self):
    #     b = Button(scale=(0, .25), text='zzz')
三月三net's avatar
三月三net 已提交
313 314 315 316 317 318 319 320

    #  if key == "escape":
    #             if mouse.locked:
    #                 self.on_disable()
    #             else:
    #                 sys.exit()

    # 按空格键则暂停
三月三net's avatar
三月三net 已提交
321
    def settings_handler_input(self, key):
三月三net's avatar
三月三net 已提交
322 323 324 325 326
        import sys
        if key == "escape":
            sys.exit()
        # print(key)
        elif key == 'space':
三月三net's avatar
三月三net 已提交
327
            self.wp.enabled = not self.wp.enabled
三月三net's avatar
三月三net 已提交
328 329
        elif key == 'left mouse down':
            print(key)
三月三net's avatar
三月三net 已提交
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
        #     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()