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

Python超人-宇宙模拟器

上级 7849c22e
...@@ -2,17 +2,18 @@ ...@@ -2,17 +2,18 @@
# title :三体二向箔场景模拟 # title :三体二向箔场景模拟
# description :三体二向箔场景模拟 # description :三体二向箔场景模拟
# author :Python超人 # author :Python超人
# date :2023-10-11 # date :2023-10-27
# link :https://gitcode.net/pythoncr/ # link :https://gitcode.net/pythoncr/
# python_version :3.9 # python_version :3.9
# ============================================================================== # ==============================================================================
import time import time
from ursina import camera, application from ursina import camera, application, lerp
from bodies import Sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto from bodies import Sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto
from common.celestial_data_service import get_init_pos_vels, init_bodies_pos_vels from common.celestial_data_service import get_init_pos_vels, init_bodies_pos_vels
from common.consts import SECONDS_PER_WEEK, AU from common.consts import SECONDS_PER_WEEK, AU
from common.image_utils import resize_image
from objs import QuadObj, CircleObj, Obj from objs import QuadObj, CircleObj, Obj
from sim_scenes.func import camera_look_at, two_bodies_colliding from sim_scenes.func import camera_look_at, two_bodies_colliding
from sim_scenes.func import ursina_run, create_sphere_sky from sim_scenes.func import ursina_run, create_sphere_sky
...@@ -30,21 +31,30 @@ class TwoWayFoilSim: ...@@ -30,21 +31,30 @@ class TwoWayFoilSim:
def build_solar_system(self): def build_solar_system(self):
# region 构建太阳系 # region 构建太阳系
self.sun = Sun(size_scale=0.8e2) self.sun, self.mercury, self.venus, self.earth, self.mars, self.jupiter, \
self.saturn, self.uranus, self.neptune, self.pluto = \
Sun(size_scale=0.8e2), \
Mercury(size_scale=2e3, distance_scale=1.5), \
Venus(size_scale=2e3, distance_scale=1.5), \
Earth(size_scale=2e3, distance_scale=1.5, rotate_angle=0), \
Mars(size_scale=2e3, distance_scale=1.4), \
Jupiter(size_scale=0.3e3, distance_scale=0.72), \
Saturn(size_scale=0.3e3, distance_scale=0.52), \
Uranus(size_scale=0.8e3, distance_scale=0.36), \
Neptune(size_scale=0.8e3, distance_scale=0.27), \
Pluto(size_scale=10e3, distance_scale=0.23)
self.bodies = [ self.bodies = [
self.sun, # 太阳放大 80 倍 self.sun, self.mercury, self.venus, self.earth, self.mars, self.jupiter,
Mercury(size_scale=4e3, distance_scale=1.3), # 水星放大 4000 倍,距离放大 1.3 倍 self.saturn, self.uranus, self.neptune, self.pluto
Venus(size_scale=4e3, distance_scale=1.3), # 金星放大 4000 倍,距离放大 1.3 倍
Earth(size_scale=4e3, distance_scale=1.3, rotate_angle=0), # 地球放大 4000 倍,距离放大 1.3 倍
Mars(size_scale=4e3, distance_scale=1.2), # 火星放大 4000 倍,距离放大 1.2 倍
Jupiter(size_scale=0.68e3, distance_scale=0.72), # 木星放大 680 倍,距离缩小到真实距离的 0.72
Saturn(size_scale=0.68e3, distance_scale=0.52), # 土星放大 680 倍,距离缩小到真实距离的 0.52
Uranus(size_scale=1.5e3, distance_scale=0.36), # 天王星放大 1500 倍,距离缩小到真实距离的 0.36
Neptune(size_scale=1.5e3, distance_scale=0.27), # 海王星放大 1500 倍,距离缩小到真实距离的 0.27
Pluto(size_scale=10e3, distance_scale=0.23), # 冥王星放大 10000 倍,距离缩小到真实距离的 0.23(从太阳系的行星中排除)
] ]
# endregion # endregion
self.two_way_foil_dt_factor = 1
self.sun.two_way_foil_dt = 0.003 * self.two_way_foil_dt_factor
self.saturn.two_way_foil_dt = 0.005 * self.two_way_foil_dt_factor
self.uranus.two_way_foil_dt = 0.002 * self.two_way_foil_dt_factor
init_bodies_pos_vels(self.bodies) init_bodies_pos_vels(self.bodies)
def build_two_way_foil(self): def build_two_way_foil(self):
...@@ -94,6 +104,61 @@ class TwoWayFoilSim: ...@@ -94,6 +104,61 @@ class TwoWayFoilSim:
ext_fun_for_method(self.two_way_foil.planet, after_run_fun=change_two_way_foil) ext_fun_for_method(self.two_way_foil.planet, after_run_fun=change_two_way_foil)
# def flatten_animation(self, body):
# """
# 天体二维化的动画
# @param body: 天体
# @return:
# """
#
# def flatten_update(planet):
# def warp():
# # 原始的 update 方法中有计算天体的运行
# planet.original_update()
#
# # 对Y轴进行压平动画,如果压平大小不足 1/50,则继续压缩,直到压缩到 1/50 就不压缩了
# # (如果觉得 1/50 的厚度压的不够,还可以继续压缩,基本上就够了)
# if planet.scale_y_v > planet.init_scale_y / 50:
# planet.scale_y_v /= 1.01
# # 灯光关闭,不然压到2纬就会是黑色
# planet.set_light_off(True)
# planet.scale_y = planet.scale_y_v
#
# return warp
#
# body.is_2d = True
# # 压平时,转速将为以前的 1/20
# body.planet.rotation_speed /= 20
# # 记录原始的厚度大小
# body.planet.scale_y_v = body.planet.scale_y
# body.planet.init_scale_y = body.planet.scale_y
# # 原始的 update 方法中有计算天体的运行,需要保留
# body.planet.original_update = body.planet.update
# # 替换 update
# body.planet.update = flatten_update(body.planet)
def gen_pixcel_image(self, planet):
"""
将纹理图改为像素图片
@param planet:
@return:
"""
import os
if planet.texture is None:
return
in_img = str(planet.texture.path)
if "_pixcel" in in_img:
return
out_img = os.path.split(in_img)
out_file = out_img[1].split('.')
out_file = f'{out_file[0]}_pixcel.{out_file[1]}'
out_img = os.path.abspath(os.path.join(out_img[0], "temp", out_file))
if not os.path.exists(out_img):
resize_image(in_img, out_img, 50, 25)
from ursina import Texture
planet.texture = Texture(out_img)
return out_img
def flatten_animation(self, body): def flatten_animation(self, body):
""" """
天体二维化的动画 天体二维化的动画
...@@ -110,22 +175,39 @@ class TwoWayFoilSim: ...@@ -110,22 +175,39 @@ class TwoWayFoilSim:
# (如果觉得 1/50 的厚度压的不够,还可以继续压缩,基本上就够了) # (如果觉得 1/50 的厚度压的不够,还可以继续压缩,基本上就够了)
if planet.scale_y_v > planet.init_scale_y / 50: if planet.scale_y_v > planet.init_scale_y / 50:
planet.scale_y_v /= 1.01 planet.scale_y_v /= 1.01
else:
self.gen_pixcel_image(planet)
planet.scale_x_v += 0.05
# 灯光关闭,不然压到2纬就会是黑色 # 灯光关闭,不然压到2纬就会是黑色
planet.set_light_off(True) planet.set_light_off(True)
planet.scale_y = planet.scale_y_v planet.scale_y = planet.scale_y_v
planet.scale_x = planet.scale_x_v
planet.scale_z = planet.scale_x_v
return warp return warp
body.is_2d = True
# 压平时,转速将为以前的 1/20 # 压平时,转速将为以前的 1/20
body.planet.rotation_speed /= 20 body.planet.rotation_speed /= 20
# 记录原始的厚度大小 # 记录原始的厚度大小
body.planet.scale_y_v = body.planet.scale_y body.planet.scale_y_v = body.planet.scale_y
body.planet.init_scale_y = body.planet.scale_y body.planet.init_scale_y = body.planet.scale_y
body.planet.scale_x_v = body.planet.scale_x
body.planet.init_scale_x = body.planet.scale_x
# 原始的 update 方法中有计算天体的运行,需要保留 # 原始的 update 方法中有计算天体的运行,需要保留
body.planet.init_update = body.planet.update body.planet.init_update = body.planet.update
# 替换 update # 替换 update
body.planet.update = flatten_update(body.planet) body.planet.update = flatten_update(body.planet)
def get_target(self):
for body in self.bodies:
if hasattr(body, "is_2d"):
continue
return body
return None
def stage_01(self): def stage_01(self):
""" """
二向箔飞向太阳 二向箔飞向太阳
...@@ -135,10 +217,31 @@ class TwoWayFoilSim: ...@@ -135,10 +217,31 @@ class TwoWayFoilSim:
# 如果二向箔和太阳碰撞 # 如果二向箔和太阳碰撞
if two_bodies_colliding(self.two_way_foil, self.sun): if two_bodies_colliding(self.two_way_foil, self.sun):
# 隐藏原始二向箔,保持在原地,不在飞行 # 隐藏原始二向箔,保持在原地,不在飞行
self.two_way_foil.planet.enabled = False # self.two_way_foil.planet.enabled = False
self.two_way_foil.init_velocity = [0, 0, 0] self.two_way_foil.init_velocity = [0, 0, 0]
# 圆形二向箔显示并设置透明度为0.9
self.two_way_foil_circle.planet.alpha = 0.9 def two_way_foil_update():
target = self.get_target()
# 让实体A朝向实体B
# self.two_way_foil.planet.look_at(target)
if target is not None:
# 让实体A向着实体B移动
dt = target.two_way_foil_dt if hasattr(target, "two_way_foil_dt") \
else 0.003 * self.two_way_foil_dt_factor
# current_time = time.time()
# if current_time - self.two_way_foil.last_time >= 0.1:
# dt = dt * 2
# self.two_way_foil.last_time = current_time
# target.two_way_foil_dt = dt
self.two_way_foil.planet.position = \
lerp(self.two_way_foil.planet.position, target.planet.position, dt)
else:
self.two_way_foil.planet.enabled = False
self.two_way_foil.planet.update = two_way_foil_update
# 圆形二向箔显示并设置透明度为0.8
self.two_way_foil_circle.planet.alpha = 0.8
self.two_way_foil_circle.planet.enabled = True self.two_way_foil_circle.planet.enabled = True
# 当前阶段为 stage_02:二向箔压平天体的阶段 # 当前阶段为 stage_02:二向箔压平天体的阶段
self.current_stage = self.stage_02 self.current_stage = self.stage_02
...@@ -148,6 +251,7 @@ class TwoWayFoilSim: ...@@ -148,6 +251,7 @@ class TwoWayFoilSim:
二向箔压平天体(二维化)的阶段 二向箔压平天体(二维化)的阶段
@return: @return:
""" """
self.two_way_foil.planet.enabled = False
# self.sun.two_dim.planet.init_scale += 0.05 # self.sun.two_dim.planet.init_scale += 0.05
# 圆形二向箔不断扩展变大 # 圆形二向箔不断扩展变大
self.two_way_foil_circle.planet.init_scale += 0.8 self.two_way_foil_circle.planet.init_scale += 0.8
...@@ -185,8 +289,20 @@ class TwoWayFoilSim: ...@@ -185,8 +289,20 @@ class TwoWayFoilSim:
if self.two_way_foil.planet.enabled: if self.two_way_foil.planet.enabled:
self.two_way_foil.planet.rotation_x += 0.1 self.two_way_foil.planet.rotation_x += 0.1
self.two_way_foil.planet.rotation_y += 1 self.two_way_foil.planet.rotation_y += 1
# # 摄像机始终看向二向箔
# camera_look_at(self.two_way_foil)
target = self.get_target()
if target is not None:
if target is self.sun:
# 摄像机始终看向二向箔 # 摄像机始终看向二向箔
camera_look_at(self.two_way_foil) camera_look_at(self.two_way_foil)
else:
# 摄像机始终看向二向箔
camera_look_at(self.two_way_foil, rotation_z=0)
camera.position = \
lerp(camera.position, target.planet.position, 0.001)
self.current_stage() self.current_stage()
...@@ -207,7 +323,7 @@ if __name__ == '__main__': ...@@ -207,7 +323,7 @@ if __name__ == '__main__':
ursina_run(sim.bodies, ursina_run(sim.bodies,
SECONDS_PER_WEEK, SECONDS_PER_WEEK,
# position=(0, 2 * AU, -11 * AU), # position=(0, 2 * AU, -11 * AU),
position=(0, 0.5 * AU, -5 * AU), position=(0, 5 * AU, -5 * AU),
cosmic_bg='', cosmic_bg='',
bg_music='sounds/no_glory.mp3', bg_music='sounds/no_glory.mp3',
show_camera_info=False, show_camera_info=False,
......
# -*- coding:utf-8 -*-
# title :三体二向箔场景模拟
# description :三体二向箔场景模拟
# author :Python超人
# date :2023-10-11
# link :https://gitcode.net/pythoncr/
# python_version :3.9
# ==============================================================================
import time
from ursina import camera, application
from bodies import Sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto
from common.celestial_data_service import get_init_pos_vels, init_bodies_pos_vels
from common.consts import SECONDS_PER_WEEK, AU
from objs import QuadObj, CircleObj, Obj
from sim_scenes.func import camera_look_at, two_bodies_colliding
from sim_scenes.func import ursina_run, create_sphere_sky
from simulators.ursina.ursina_event import UrsinaEvent
from simulators.func import ext_fun_for_method
class TwoWayFoilSim:
"""
三体二向箔场景模拟
"""
def __init__(self):
self.current_stage = self.stage_01
def build_solar_system(self):
# region 构建太阳系
self.sun, self.mercury, self.venus, self.earth, self.mars, self.jupiter, \
self.saturn, self.uranus, self.neptune, self.pluto = \
Sun(size_scale=0.8e2), \
Mercury(size_scale=4e3, distance_scale=1.3), \
Venus(size_scale=4e3, distance_scale=1.3), \
Earth(size_scale=4e3, distance_scale=1.3, rotate_angle=0), \
Mars(size_scale=4e3, distance_scale=1.2), \
Jupiter(size_scale=0.68e3, distance_scale=0.72), \
Saturn(size_scale=0.68e3, distance_scale=0.52), \
Uranus(size_scale=1.5e3, distance_scale=0.36), \
Neptune(size_scale=1.5e3, distance_scale=0.27), \
Pluto(size_scale=10e3, distance_scale=0.23)
self.bodies = [
self.sun, self.mercury, self.venus, self.earth, self.mars, self.jupiter,
self.saturn, self.uranus, self.neptune, self.pluto
]
# endregion
init_bodies_pos_vels(self.bodies)
def build_two_way_foil(self):
"""
创建二向箔(一个原始的方形二向箔,一个不断扩展的圆形二向箔)
@return:
"""
# 原始的方形二向箔
self.two_way_foil = QuadObj(texture='two_way_foil.png',
# size_scale=4e7,
size_scale=1e7,
init_velocity=[0, -6, 150],
init_position=[0, 0.5 * AU, -10 * AU]) \
.set_light_disable(True).set_ignore_gravity(True)
# 不断扩展的圆形二向箔
self.two_way_foil_circle = CircleObj(texture="two_way_foil_circle.png",
size_scale=self.two_way_foil.size_scale * 2,
) \
.set_light_disable(True).set_ignore_gravity(True)
self.bodies.append(self.two_way_foil)
self.bodies.append(self.two_way_foil_circle)
def build(self):
self.build_solar_system()
self.build_two_way_foil()
def on_ready(self):
"""
事件绑定后,模拟器运行前会触发
@return:
"""
# 创建天空
# camera.clip_plane_near = 0.1
camera.clip_plane_far = 1000000
create_sphere_sky(scale=200000)
application.time_scale = 5
# 圆形二向箔初始化(一开始不显示)
self.two_way_foil_circle.planet.rotation_x = 90
self.two_way_foil_circle.planet.enabled = False
def change_two_way_foil():
# 长方形膜状物,长八点五厘米,宽五点二厘米,比一张信用卡略大一些,极薄,看不出任何厚度。
# 封装状态下晶莹剔透、无色透明。待机模式下由于封装力场逐渐蒸发,会发出白光,使其表面呈纯白色,看上去就是一张纸条
self.two_way_foil.planet.scale_x = 8.5
self.two_way_foil.planet.scale_y = 5.2
ext_fun_for_method(self.two_way_foil.planet, after_run_fun=change_two_way_foil)
def flatten_animation(self, body):
"""
天体二维化的动画
@param body: 天体
@return:
"""
def flatten_update(planet):
def warp():
# 原始的 update 方法中有计算天体的运行
planet.original_update()
# 对Y轴进行压平动画,如果压平大小不足 1/50,则继续压缩,直到压缩到 1/50 就不压缩了
# (如果觉得 1/50 的厚度压的不够,还可以继续压缩,基本上就够了)
if planet.scale_y_v > planet.init_scale_y / 50:
planet.scale_y_v /= 1.01
# 灯光关闭,不然压到2纬就会是黑色
planet.set_light_off(True)
planet.scale_y = planet.scale_y_v
return warp
# 压平时,转速将为以前的 1/20
body.planet.rotation_speed /= 20
# 记录原始的厚度大小
body.planet.scale_y_v = body.planet.scale_y
body.planet.init_scale_y = body.planet.scale_y
# 原始的 update 方法中有计算天体的运行,需要保留
body.planet.original_update = body.planet.update
# 替换 update
body.planet.update = flatten_update(body.planet)
def stage_01(self):
"""
二向箔飞向太阳
@return:
"""
# 如果二向箔和太阳碰撞
if two_bodies_colliding(self.two_way_foil, self.sun):
# 隐藏原始二向箔,保持在原地,不在飞行
self.two_way_foil.planet.enabled = False
self.two_way_foil.init_velocity = [0, 0, 0]
self.two_way_foil.planet.original_update = lambda: None
# 圆形二向箔显示并设置透明度为0.9
self.two_way_foil_circle.planet.alpha = 0.9
self.two_way_foil_circle.planet.enabled = True
# 当前阶段为 stage_02:二向箔压平天体的阶段
self.current_stage = self.stage_02
def stage_02(self):
"""
二向箔压平天体(二维化)的阶段
@return:
"""
# self.sun.two_dim.planet.init_scale += 0.05
# 圆形二向箔不断扩展变大
self.two_way_foil_circle.planet.init_scale += 0.8
# 调整天体二维化的时间,一般需要延时,保证扩展和二维化同步的真实效果
two_way_delay_times = [0.5, # 太阳
0.5, 0.8, 1.0, # 水星 金星 地球
1.2, 2.0, 3.0, # 火星 木星 土星
4.0, 4.5, 5.5] # 天王星 海王星 冥王星
for idx, b in enumerate(self.bodies):
if isinstance(b, Obj):
# 二向箔不处理
continue
if hasattr(b, "two_way_time"):
# 二向箔和天体碰撞的时间不为空,则说明已经碰撞
if b.two_way_time is not None:
# 如果碰撞后的延时时间到,则进行压平天体处理(二维化)
if time.time() - b.two_way_time > two_way_delay_times[idx]:
self.flatten_animation(b)
# 二向箔和天体碰撞的时间设置为空,就是说明二维化结束
b.two_way_time = None
elif two_bodies_colliding(self.two_way_foil_circle, b):
# 二向箔和天体碰撞,但暂时先不二维化,记下时间,延时二维化
b.two_way_time = time.time()
# 圆形二向箔不断旋转的效果
self.two_way_foil_circle.planet.rotation_z += 0.4
def on_timer_changed(self, time_data):
"""
@param time_data:
@return:
"""
# 原始方形二向箔飞行的翻转效果
if self.two_way_foil.planet.enabled:
self.two_way_foil.planet.rotation_x += 0.1
self.two_way_foil.planet.rotation_y += 1
# 摄像机始终看向二向箔
camera_look_at(self.two_way_foil)
self.current_stage()
if __name__ == '__main__':
"""
三体二向箔场景模拟
"""
sim = TwoWayFoilSim()
sim.build()
# 订阅事件后,上面2个函数功能才会起作用
# 运行中,每时每刻都会触发 on_timer_changed
UrsinaEvent.on_timer_changed_subscription(sim.on_timer_changed)
# 运行前会触发 on_ready
UrsinaEvent.on_ready_subscription(sim.on_ready)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(sim.bodies,
SECONDS_PER_WEEK,
# position=(0, 2 * AU, -11 * AU),
position=(0, 0.5 * AU, -5 * AU),
cosmic_bg='',
bg_music='sounds/no_glory.mp3',
show_camera_info=False,
show_control_info=False,
timer_enabled=True,
show_grid=False)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册