# -*- coding:utf-8 -*- # title :太阳系中消失的行星 # description :太阳系中消失的行星 # author :Python超人 # date :2023-12-05 # link :https://gitcode.net/pythoncr/ # python_version :3.9 # ============================================================================== import time from bodies import Sun, Mercury, Venus, Earth, Mars, Moon, Ceres, Jupiter, Saturn, Uranus, Neptune, Pluto, Asteroids from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_MONTH, SECONDS_PER_YEAR, AU from objs import CoreValagaClas, ScifiGunship, SpaceShip, StarWarsSpeeder, SciFiBomber from objs.battleship import BattleShip from sim_scenes.func import mayavi_run, ursina_run, create_sphere_sky from sim_scenes.universe_sim_scenes import UniverseSimScenes from simulators.func import ext_fun_for_method from simulators.ursina.ursina_config import UrsinaConfig from simulators.ursina.ursina_event import UrsinaEvent from ursina import camera, Vec3, distance import math class TheLostPlanetSim(UniverseSimScenes): def __init__(self): self.sun = Sun(size_scale=6e2).set_resolution(60) # 太阳放大 500 倍 self.sun.glows = (30, 1.005, 0.03) # self.asteroids = Asteroids(size_scale=1.08e2, parent=self.sun) # 小行星模拟(仅 ursina 模拟器支持) # 环状星群带(inner_radius, outer_radius, subdivisions) # inner_radius:内圆半径 outer_radius:外圆半径,subdivisions:细分数,控制圆环的细节和精度 # self.asteroids.torus_zone = 4.7, 5.5, 64 self.moon = Moon(size_scale=3.5e3, init_position=[0, 0, (0.5 + 2.5) * AU], distance_scale=1.76) # ceres = Ceres(size_scale=3e3, distance_scale=1.7) self.mercury = Mercury(size_scale=3e3, distance_scale=8.5) self.venus = Venus(size_scale=3e3, distance_scale=5) self.earth = Earth(size_scale=3e3, distance_scale=4.05) self.mars = Mars(size_scale=3e3, distance_scale=2.95) self.jupiter = Jupiter(size_scale=0.68e3, distance_scale=1.12) self.saturn = Saturn(size_scale=0.68e3, distance_scale=0.74) # self.ship = ScifiGunship(name="飞船", mass=1e30, color=(111, 140, 255), # init_position=self.mars.init_position, # init_velocity=[0, 0, 0], # size_scale=2e4, distance_scale=2.9). \ # set_ignore_gravity(True).set_light_disable(True) self.bodies = [ self.sun, self.mercury, # 水星放大 4000 倍 self.venus, # 金星放大 4000 倍 self.earth, # 地球放大 4000 倍 self.mars, # 火星放大 4000 倍 # asteroids, self.moon, # ceres, # Earth(size_scale=3e3, init_position=[0, 0, (2.17) * AU]), # 地球放大 4000 倍 # Earth(size_scale=3e3, init_position=[0, 0, (3.64) * AU]), # 地球放大 4000 倍 self.jupiter, # 木星放大 680 倍 self.saturn, # 土星放大 680 倍 Uranus(size_scale=0.8e3, distance_scale=0.43), # 天王星放大 800 倍 Neptune(size_scale=1e3, distance_scale=0.3), # 海王星放大 1000 倍 ] # 水星 0.4AU # 金星 0.4+0.3 AU # 地球 0.4+0.6 AU # 火星 0.4+1.2 AU # 木星 0.4+4.8 AU # 土星 0.4+9.6 AU # 天王星 0.4+19.2 AU # d = (n+4)/10 # an = 0.4+0.3×(2n-2) planet_no = -1 for idx, body in enumerate(self.bodies): if isinstance(body, Asteroids): continue body.init_position = [-body.init_position[2], 0, 0] body.rotation_speed = 0 planet_no += 1 # body.init_velocity = [0, 0, 0] # an = 0.4 + 0.3 * pow(2,idx) # an = (idx+4)/10 # 其中k=0,1,2,4,8,16,32,64,128 (0以后数字为2的2次方) # 行星 公式推得值 实测值 # 金星 0.7 0.72 # 地球 1 1 # 火星 1.6 1.52 # 谷神星 2.8 2.9 [1] # 木星 5.2 5.2 # 土星 10 9.54 # 天王星 19.6 19.18 # 海王星 38.8 30.06 # 冥王星 77.2 39.44 # 提丢斯-波得定则 # https://baike.baidu.com/item/%E6%8F%90%E4%B8%A2%E6%96%AF-%E6%B3%A2%E5%BE%97%E5%AE%9A%E5%88%99/859407 # 小行星 2.17-3.64天文单位 if planet_no == 0: continue elif planet_no == 1: an = 0.4 else: an = 0.4 + 0.3 * pow(2, planet_no - 2) # print(body.name, an, body.position[2] / AU) # self.bodies += [self.ship] self.step_index = 0 self.init_steps() def on_ready(self): """ 事件绑定后,模拟器运行前会触发 @return: """ from ursina import camera, Vec3, application # 创建天空 create_sphere_sky(scale=20000, rotation_x=0, rotation_y=170) camera.clip_plane_near = 0.1 camera.clip_plane_far = 1000000 # self.show_grid_axises(50) # camera.look_at(self.jupiter.planet) # camera.look_at(sun.planet) # camera.rotation_z -= 90 # # self.moon.planet.update = lambda :None # def moon_update(): # self.moon.planet.scale = 80 # # self.moon.planet.rotation_y += 10 # # ext_fun_for_method(self.moon.planet, after_run_fun=moon_update) # for i in range(10): # time.sleep(0.1) # create_asteroid() # UniverseSimScenes.show_grid_axises() # self.asteroids.planet.enabled = False self.moon.planet.enabled = False application.time_scale = 0.1 # ceres.planet.enabled = False # self.moon.planet.look_at(self.mars.planet) def set_alpha_animation(self, body, begin_alpha, end_alpha, interval, is_destroy=False): from ursina import destroy if hasattr(body, "planet"): planet = body.planet else: planet = body planet.alpha = begin_alpha if begin_alpha > end_alpha: interval = -abs(interval) else: interval = abs(interval) origin_update = planet.update def alpha_animation(): from ursina import camera, Vec4 origin_update() alpha = planet.alpha alpha += interval if (interval > 0 and alpha >= end_alpha) or (interval < 0 and alpha <= end_alpha): alpha = end_alpha planet.update = origin_update planet.enabled = (alpha > 0) planet.alpha = alpha if is_destroy and not planet.enabled: destroy(planet) # planet.color = Vec4(planet.color[0], planet.color[1], planet.color[2], alpha) # print(body, planet.alpha, planet.rotation_x, planet.rotation_y, planet.rotation_z) planet.update = alpha_animation # ext_fun_for_method(planet, after_run_fun=alpha_animation) def create_asteroid(self, init_angle=110, smooth=False): from ursina import Entity, color, Vec3 import math import random pos = self.moon.planet.position # + Vec3() # min(self.moon.planet.position) radius = min(self.moon.planet.position) + 50 * random.random() - 15 # * moon.distance_scale asteroid = Entity(model='sphere', position=pos, color=color.white, scale=1.5) asteroid.s_angle = init_angle asteroid.init_pos = pos y = 10 * random.random() - 5 speed = random.random() / 7 + 0.2 def rotation(): angle = math.pi * asteroid.s_angle / 180 x = self.sun.planet.x + radius * math.cos(angle) z = self.sun.planet.z + radius * math.sin(angle) target_pos = Vec3(x, y, z) if asteroid.init_pos is None or not smooth: # angle = math.pi * asteroid.s_angle / 180 # x = self.sun.planet.x + radius * math.cos(angle) # z = self.sun.planet.z + radius * math.sin(angle) asteroid.position = target_pos else: asteroid.look_at(target_pos) asteroid.position += asteroid.forward * 1.5 d = distance(asteroid.position, target_pos) if d < 3: asteroid.init_pos = None asteroid.s_angle += speed asteroid.update = rotation return asteroid def create_asteroids(self): self.asteroids = [] for i in range(400): self.asteroids.append(self.create_asteroid(i)) # def clear_asteroids(self): # for a in self.asteroids: # a.enabled = False def asteroid_fade_in(self): """ 小行星群渐渐显示 @return: """ self.create_asteroids() for a in self.asteroids: self.set_alpha_animation(a, 0.0, 1.0, 0.01) def asteroid_fade_out(self): """ 小行星群渐渐消失 @return: """ for a in self.asteroids: self.set_alpha_animation(a, 1.0, 0.0, 0.01, is_destroy=True) def init_steps(self): def earth_orbit_the_sun(): self.body_orbit_the_sun(self.earth, 90) def mars_orbit_the_sun(): self.body_orbit_the_sun(self.mars, 90) def venus_orbit_the_sun(): self.body_orbit_the_sun(self.venus, 90) def mercury_orbit_the_sun(): self.body_orbit_the_sun(self.mercury, 90) def jupter_orbit_the_sun(): self.body_orbit_the_sun(self.jupiter, 90) def saturn_orbit_the_sun(): self.body_orbit_the_sun(self.saturn, 90) self.steps = [ (mercury_orbit_the_sun, 100, 1), (venus_orbit_the_sun, 100, 1), (earth_orbit_the_sun, 100, 1), (mars_orbit_the_sun, 600, 1), (jupter_orbit_the_sun, 100, 1), (saturn_orbit_the_sun,600, 1), (self.asteroid_fade_in, 900, 1), (self.asteroid_fade_out, 900, 1), # fun, wait_days, run_times (self.camera_back_1, 900, -1), (self.camera_back_2, -1, -1), (lambda: None, -1, -1) ] def body_orbit_the_sun(self, body, start_angle, angle_speed=0.5): if not hasattr(body, "orbit_radius"): body.orbit_radius = body.position[0] * UrsinaConfig.SCALE_FACTOR * body.distance_scale body.orbit_angle = start_angle # origin_update = body.planet.update def orbit_update(): if body.orbit_angle > 180: body.orbit_angle = 180 body.planet.update = lambda: None angle = math.pi * body.orbit_angle / 180 x = body.orbit_radius * math.cos(angle) z = body.orbit_radius * math.sin(angle) body.planet.main_entity.position = Vec3(x, 0, z) body.orbit_angle += angle_speed print(body.orbit_angle) body.planet.update = orbit_update def camera_back_1(self): if camera.position[2] < 460: camera.position += camera.back def camera_back_2(self): if camera.position[2] < 550: # camera.position += camera.right camera.position += camera.back def on_timer_changed(self, time_data): # camera.position += camera.right if time_data.total_days > 0: if self.step_index > len(self.steps) - 1: self.step_index = len(self.steps) - 1 fun, wait_days, run_times = self.steps[self.step_index] if not hasattr(self, f"{fun.__name__}_wait_days"): setattr(self, f"{fun.__name__}_wait_days", time_data.total_days) setattr(self, f"{fun.__name__}_run_times", 0) fun_run_times = getattr(self, f"{fun.__name__}_run_times") if fun_run_times < run_times or run_times < 0: fun() setattr(self, f"{fun.__name__}_run_times", fun_run_times + 1) fun_wait_days = getattr(self, f"{fun.__name__}_wait_days") if wait_days + fun_wait_days < time_data.total_days and wait_days > 0: self.step_index += 1 # print(self.step_index, fun.__name__) if __name__ == '__main__': sim = TheLostPlanetSim() # UniverseSimScenes.set_window_size((1920, 1079), False) # 运行前会触发 on_ready UrsinaEvent.on_ready_subscription(sim.on_ready) # UrsinaEvent.after_ready_subscription(after_ready) UrsinaEvent.on_timer_changed_subscription(sim.on_timer_changed) # 使用 ursina 查看的运行效果 # 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹 # position = 左-右+、上+下-、前+后- ursina_run(sim.bodies, SECONDS_PER_YEAR, gravity_works=False, show_exit_button=False, show_control_info=False, show_camera_info=False, # position=(0, 2 * AU, -11 * AU), # position=(0, 20 * AU, 10 * AU), # position=(4.5 * AU, AU, 5 * AU), position=(5 * AU, AU / 2, -5 * AU), timer_enabled=True, # show_timer=True, cosmic_bg='', # show_trail=True, show_grid=False)