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

Python超人-宇宙模拟器

上级 f7b9372b
# -*- coding:utf-8 -*-
# title :虫洞效果模拟
# description :虫洞效果模拟
# author :Python超人
# date :2023-11-19
# link :https://gitcode.net/pythoncr/
# python_version :3.9
# ==============================================================================
import random
from bodies import Sun, Earth, FixedStar
from sim_scenes.universe_sim_scenes import UniverseSimScenes
from simulators.func import ext_fun_for_method
from simulators.ursina.entities.entity_utils import create_ambient_light
from bodies.universe_body import create_universe_body
from common.consts import SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR, AU
from objs import Obj, SpaceShip, StarWarsSpeeder, ScifiGunship, SciFiBomber, CoreValagaClas
from sim_scenes.func import mayavi_run, mpl_run, ursina_run, camera_look_at, two_bodies_colliding, camera_move_control, \
get_run_speed_factor, create_text_panel
from ursina import camera, application, lerp, Vec3
from bodies import Body
from simulators.ursina.ursina_event import UrsinaEvent
from dataclasses import dataclass, field
import inspect
@dataclass(order=True)
class WormholeInfo:
# entity: object
name: str = field(default='')
position: list[float] = field(default_factory=[0, 0, 0])
camera_target_init_speed: int = 100
camera_init_speed: int = 100
camera_target_speed: int = 1200
camera_target_max_speed: int = 1500
camera_forward_speed: int = - 0.1
camera_max_speed: int = 1500
camera_acc_speed: int = 1.02
camera_forward_acc_speed: int = 0.01
size_factor: float = 16
rotation_x: float = None
rotation_y: float = None
rotation_z: float = None
around_max_count: int = 400
around_max_speed: int = -100
around_min_speed: int = 100
around_direction: str = "right"
around_acc_speed: int = -1.5
class WormholeSim(UniverseSimScenes):
SIZE_SCALE = 1
D = AU / 10 * SIZE_SCALE
def create_universes(self):
D = self.D
# camera_target_init_speed: int = 100
# camera_init_speed: int = 100
# camera_target_speed: int = 1200
# camera_target_max_speed: int = 1500
# camera_forward_speed: int = - 0.1
# camera_max_speed: int = 1500
# camera_acc_speed: int = 1.02
# camera_forward_acc_speed: int = 0.01
# size_factor: float = 16
# rotation_x: float = None
# rotation_y: float = None
# rotation_z: float = None
# around_max_count: int = 400
# around_max_speed: int = -100
# around_min_speed: int = 100
# around_direction: str = "right"
# around_acc_speed: int = -1.5
wormhole_infos = [
{"name": "我们宇宙", "position": [D, 0, -D], "rotation_y": 100, "around_direction": "right",
"camera_forward_speed": 0, "around_max_count": 300, "camera_target_max_speed": 1000,
"camera_max_speed": 2200,
"camera_target_init_speed": 5},
{"name": "宇宙1", "position": [D, 0, D], "rotation_z": 0, "around_acc_speed": -2,
"around_min_speed": 150, "camera_forward_speed": -0.2, "around_max_count": 600},
{"name": "宇宙2", "position": [- D, -D, D], "around_direction": "up", "around_max_count": 400},
{"name": "宇宙3", "position": [- D, 0, -D], "around_max_count": 600},
{"name": "宇宙4", "position": [-D, D, D], "around_direction": "left", "around_max_count": 400},
{"name": "宇宙5", "position": [- D, -D, D], "around_direction": "up", "around_max_count": 600},
# {"position": [D, -D, D]},
# {"position": [- D, -D, -D]},
# {"position": [- D, 0, D]},
# {"position": [D, D, -D]},
# {"position": [- D, D, -D]},
# {"position": [- D, D, D]},
# {"position": [D, -D, -D]}
]
def build_wormhole_info_param(wormhole_info):
# ps = WormholeInfoParams()
if self.wormholeinfo_default_vals is None:
self.wormholeinfo_default_vals = {}
init_wormholeinfo_default_vals = True
else:
init_wormholeinfo_default_vals = False
ps = {}
attributes = inspect.getmembers(WormholeInfo)
for attr in attributes:
if not attr[0].startswith('_'):
pn = attr[0]
if init_wormholeinfo_default_vals:
self.wormholeinfo_default_vals[pn] = attr[1]
if pn not in wormhole_info.keys():
continue
ps[pn] = wormhole_info[pn] # attr[1].default
return ps
for idx, wormhole_info in enumerate(wormhole_infos):
pos = wormhole_info.get("position", [0, 0, 0])
name = wormhole_info.get("name", f"宇宙{idx}")
ps1 = build_wormhole_info_param(wormhole_info)
ps1["position"] = pos
ps1["name"] = name
universe = self.create_universe(name, idx, pos)
self.universes.append(universe)
self.wormhole_infos[universe] = WormholeInfo(**ps1)
def __init__(self, texture_dir="cosmic_pan_01"):
D = self.D
self.texture_dir = texture_dir
self.camera_target = CoreValagaClas(name="摄像机镜头", mass=1e30, color=(111, 140, 255),
# init_position=[0, 0, 0],
# init_position=[D, 0, -D],
init_position=[0, D * 1.15, -D * 2.05],
init_velocity=[0, 0, 0],
texture="core_valaga_clas_light.jpg",
# size_scale=self.SIZE_SCALE * 1e3
size_scale=self.SIZE_SCALE * 0.5e3
).set_ignore_gravity(True).set_light_disable(True)
self.wormholeinfo_default_vals = None
self.camera_target.go_target = None
self.camera_target.go_target_stage = None
self.last_universe = None
self.current_universe = None
self.target_universe = None
self.universes = []
self.fixed_stars = []
self.wormhole_infos = {}
self.create_universes()
self.create_fixed_stars()
self.bodies = [self.camera_target] + self.universes + self.fixed_stars
def create_universe(self, name, idx, position):
if name is None:
name = f"宇宙{idx}"
if self.texture_dir is not None:
texture = f'{self.texture_dir}/cosmic_pan_{idx + 1}.jpg'
else:
texture = f'cosmic_pan_{idx + 1}.jpg'
universe_body = create_universe_body(name, texture=texture, # self.textures[idx],
size_scale=self.SIZE_SCALE,
init_position=position,
init_velocity=[0, 0, 0],
)
universe_body.set_light_disable(True)
return universe_body
def fixed_stars_D(self):
r = random.random()
D = self.D * (0.5 + r * 2.5)
return D
def create_fixed_stars(self):
D = self.fixed_stars_D
positions = [[D(), D(), D()], [D(), D(), -D()], [D(), -D(), D()],
[D(), -D(), -D()], [-D(), D(), D()], [-D(), D(), -D()],
[-D(), -D(), D()], [-D(), -D(), -D()],
[D(), D(), D()], [D(), D(), -D()], [D(), -D(), D()],
[D(), -D(), -D()], [-D(), D(), D()], [-D(), D(), -D()],
[-D(), -D(), D()], [-D(), -D(), -D()],
[D(), D(), D()], [D(), D(), -D()], [D(), -D(), D()],
[D(), -D(), -D()], [-D(), D(), D()], [-D(), D(), -D()],
[-D(), -D(), D()], [-D(), -D(), -D()]
,
[D(), D(), D()], [D(), D(), -D()], [D(), -D(), D()],
[D(), -D(), -D()], [-D(), D(), D()], [-D(), D(), -D()],
[-D(), -D(), D()], [-D(), -D(), -D()]
]
# positions = [[D, D, D], [D, D, -D], [D, -D, D], [D, 0, D], [D, 0, - D], [D, 0, D],
# [D, -D, -D], [-D, D, D], [-D, D, -D], [0, -D, -D], [0, D, D], [0, D, -D],
# [-D, -D, D], [-D, -D, -D], [-D, -D, 0], [-D, -D, 0]]
for position in positions:
fixed_star = FixedStar(color=(200, 200, 200), size_scale=0.2, init_position=position)
fixed_star.glows = (0, 1.1, 1.08)
self.fixed_stars.append(fixed_star)
def init_setting(self):
camera.clip_plane_near = 0.6
# camera.clip_plane_near = 0.01
camera.clip_plane_far = 100000
# create_sphere_sky(scale=200000)
# application.time_scale = 0.0001
# application.time_scale = 0.0005
application.time_scale = 0.001
# camera.scale = 1000
# camera.parent = camera_target.planet
# camera.rotation_x = 90
camera.fov = 110
for universe in self.universes:
universe.planet.collider = "sphere"
universe.planet.enabled = False
camera.forward_speed = 0
def get_wormhole_data(self, data_name, default_val=None, universe=None):
def ret_value():
if default_val is None:
if data_name in self.wormholeinfo_default_vals.keys():
return self.wormholeinfo_default_vals[data_name]
return default_val
if universe is None:
universe = self.current_universe
wormhole_info = self.wormhole_infos[universe]
if wormhole_info is None:
return ret_value()
if not hasattr(wormhole_info, data_name):
return ret_value()
val = getattr(wormhole_info, data_name)
if val is None:
return ret_value()
return val
def universe_reset(self):
self.current_idx += 1
self.target_idx += 1
if self.current_idx > len(self.universes) - 1:
self.current_idx = 0
if self.target_idx > len(self.universes) - 1:
self.target_idx = 0
for u in self.universes:
self.scale_down(u)
u.planet.enabled = False
self.last_universe = self.current_universe
self.current_universe = self.universes[self.current_idx]
self.target_universe = self.universes[self.target_idx]
rotation_x: float = self.get_wormhole_data("rotation_x")
rotation_y: float = self.get_wormhole_data("rotation_y")
rotation_z: float = self.get_wormhole_data("rotation_z")
if rotation_x is not None:
self.current_universe.planet.rotation_x = rotation_x
if rotation_y is not None:
self.current_universe.planet.rotation_y = rotation_y
if rotation_z is not None:
self.current_universe.planet.rotation_z = rotation_z
self.camera_target.speed = self.get_wormhole_data("camera_target_init_speed")
camera.speed = self.get_wormhole_data("camera_init_speed")
if self.last_universe is None:
self.camera_target.go_target = self.target_universe
self.camera_target.go_target_stage = "target_universe"
self.camera_target.go_target_hit = False
else:
self.camera_target.go_target = self.last_universe
self.last_universe.planet.enabled = True
size_factor = self.get_wormhole_data("size_factor", universe=self.last_universe)
self.scale_down(self.last_universe, scale_size=self.SIZE_SCALE * size_factor)
self.camera_target.go_target_stage = "last_universe"
self.camera_target.go_target_hit = False
self.scale_down(u)
self.camera_target.speed = self.get_wormhole_data("camera_target_speed")
camera.forward_speed = self.get_wormhole_data("camera_forward_speed")
self.scale_up(self.current_universe)
# self.scale_down(self.target_universe)
self.current_universe.planet.enabled = True
# self.current_universe.planet.set_light_off()
self.target_universe.planet.enabled = True
# self.target_universe.planet.set_light_off()
def body_update_reset(self):
# for b in self.bodies:
# b.planet.update = lambda: None
self.camera_target.planet.update = lambda: None
def on_ready(self):
"""
事件绑定后,模拟器运行前会触发
@return:
"""
self.init_setting()
# create_ambient_light(parent=camera)
# def camera_target_scale():
# self.camera_target.planet.scale -= 10
#
# ext_fun_for_method(self.camera_target.planet, after_run_fun=camera_target_scale)
self.current_idx = -1
self.target_idx = 0
self.current_universe = None
self.body_update_reset()
self.universe_reset()
# camera_look_at(self.camera_target)
self.camera_target.planet.collider = "sphere"
# TODO:隐藏摄像机目标
self.camera_target.planet.enabled = True
camera.collider = "sphere"
camera.current_universe = self.current_universe
camera_look_at(self.camera_target, rotation_z=0)
self.text_panel = create_text_panel(font="fonts/DroidSansFallback.ttf", font_scale=1.3)
def update_text_panel(self):
"""
更新文字信息面板
@param d_sun:
@return:
"""
if not hasattr(self, "text_panel"):
return
panel_text = "虫洞穿越宇宙:\n\n当前宇宙:%s " % self.current_universe.name
panel_text += "\n\n下站宇宙:%s" % self.target_universe.name
if self.last_universe is not None:
panel_text += "\n\n上站宇宙:%s " % self.last_universe.name
self.text_panel.text = panel_text
def scale_up(self, obj):
if obj is None:
return
obj.planet.init_scale = self.SIZE_SCALE * 100
def scale_down(self, obj, scale_size=None):
if obj is None:
return
if scale_size is None:
scale_size = self.SIZE_SCALE
obj.planet.init_scale = scale_size
def around_universe(self, around_target):
around_max_count = self.get_wormhole_data("around_max_count")
around_max_speed = self.get_wormhole_data("around_max_speed")
around_min_speed = self.get_wormhole_data("around_min_speed")
around_direction = self.get_wormhole_data("around_direction")
around_acc_speed = self.get_wormhole_data("around_acc_speed")
# around_target = camera
self.camera_target.planet.look_at(around_target)
# print(self.camera_target.planet.rotation_x,self.camera_target.planet.rotation_y,self.camera_target.planet.rotation_z)
planet_direction = getattr(self.camera_target.planet, around_direction)
if not hasattr(self.camera_target, "around_speed"):
self.camera_target.around_speed = 0
if not hasattr(self.camera_target, "around_count"):
self.camera_target.around_count = 0
if self.camera_target.around_count > around_max_count:
if hasattr(self.camera_target, "around_speed") and \
abs(self.camera_target.around_speed) > abs(around_min_speed):
self.camera_target.around_speed -= around_acc_speed
else:
# delattr(self.camera_target, "around_speed")
delattr(self.camera_target, "around_count")
self.camera_target.go_target_stage = "last_universe"
self.camera_target.speed = self.get_wormhole_data("camera_init_speed")
else:
self.camera_target.around_speed += around_acc_speed
if hasattr(self.camera_target, "around_speed"):
if abs(self.camera_target.around_speed) > abs(around_max_speed):
self.camera_target.around_speed = around_max_speed
self.camera_target.planet.position += planet_direction * self.camera_target.around_speed
print("around_speed", self.camera_target.around_speed)
if hasattr(self.camera_target, "around_count"):
self.camera_target.around_count += 1
# camera.forward_speed = -0.05
if abs(camera.forward_speed) > 0.01:
camera_forward_acc_speed = self.get_wormhole_data("camera_forward_acc_speed")
camera.forward_speed += camera_forward_acc_speed
# else:
# camera.forward_speed = 0
def go_last_universe(self, go_target):
if self.camera_target.planet.intersects(go_target).hit:
if not self.camera_target.go_target_hit:
self.camera_target.go_target_hit = True
self.camera_target.go_target_stage = "around_universe"
self.camera_target.go_target = self.target_universe
self.camera_target.speed = self.get_wormhole_data("camera_init_speed")
camera.speed = 1 # 在看上一个宇宙时候,停留一会
# # camera.forward_speed = -0.05
# if camera.forward_speed < 0:
# camera_forward_acc_speed = self.get_wormhole_data("camera_forward_acc_speed")
# camera.forward_speed += camera_forward_acc_speed
# else:
# camera.forward_speed = 0
if hasattr(self.camera_target, "around_speed"):
if abs(self.camera_target.around_speed) > abs(10):
around_acc_speed = self.get_wormhole_data("around_acc_speed")
around_direction = self.get_wormhole_data("around_direction")
planet_direction = getattr(self.camera_target.planet, around_direction)
self.camera_target.around_speed -= around_acc_speed
self.camera_target.planet.position += planet_direction * self.camera_target.around_speed
else:
delattr(self.camera_target, "around_speed")
def camera_target_circle_glow(self):
from simulators.ursina.ursina_mesh import create_circle
from common.image_utils import find_texture
# self.camera_target.planet.enabled = False
texture = find_texture("circle_ring.png")
c = create_circle(parent=None, pos=self.camera_target.planet.position, scale=1, texture=texture)
def c_blink():
alpha = c.alpha
if c.alpha_direction == 1:
alpha += 0.08
if alpha >= 1:
c.alpha_direction = 0
alpha = 1
else:
alpha -= 0.08
print("alpha", alpha)
c.alpha = alpha
c.scale = alpha
if c.alpha <= 0.9 and c.alpha_direction == 0 and self.camera_target.planet.enabled:
self.camera_target.planet.enabled = False
if c.alpha <= 0:
c.enabled = False
c.update = lambda: None
c.set_light_off(True)
c.look_at(camera)
c.alpha = 0.01
c.scale = 0.01
c.alpha_direction = 1
c.update = c_blink
# self.camera_target
def on_timer_changed(self, time_data):
self.target_universe.planet.rotation_y -= 0.5
if self.last_universe is not None:
self.last_universe.planet.rotation_y += 0.3
camera_look_at(self.camera_target, rotation_z=0)
# camera.rotation_x -= 2
# self.camera_target.planet.look_at(self.target_universe.planet)
# self.camera_move(time_data)
from ursina import time, distance
camera_max_speed = self.get_wormhole_data("camera_max_speed")
camera_acc_speed = self.get_wormhole_data("camera_acc_speed")
camera_target_max_speed = self.get_wormhole_data("camera_target_max_speed", )
if self.camera_target.go_target is not None:
go_target = self.camera_target.go_target.planet
if self.camera_target.go_target_stage == "around_universe":
# 摄像机镜头环绕目标宇运动(摄像机镜头暂时不去目标)
self.around_universe(go_target)
else:
# 摄像机镜头看向目标宇宙,并逐渐靠近目标
self.camera_target.planet.look_at(go_target)
self.camera_target.planet.position = \
lerp(self.camera_target.planet.position, go_target.position,
self.camera_target.speed * time.dt)
# 摄像机镜头加速度计算(摄像机镜头圆滑效果),并对摄像机镜头移动速度进行限制
self.camera_target.speed *= camera_acc_speed
if self.camera_target.speed > camera_target_max_speed:
self.camera_target.speed = camera_target_max_speed
if self.camera_target.go_target_stage == "last_universe":
# 进入新的宇宙后,摄像机回看上一个宇宙的镜头
self.go_last_universe(go_target)
elif self.camera_target.go_target_stage == "target_universe":
# 摄像机镜头去往新的宇宙进行中
if self.camera_target.planet.intersects(go_target).hit and not self.camera_target.go_target_hit:
# 摄像机镜头达到新的目标宇宙后,摄像机就不后退了
self.camera_target.go_target_hit = True
camera.forward_speed = 0
# TODO:隐藏摄像机目标
self.camera_target_circle_glow()
# camera.speed = 3000 # TODO:这个不能反复执行
# self.camera_target.planet.position += self.camera_target.planet.forward*100
# print(self.camera_target.planet.position)
# 摄像机加速度计算(摄像机圆滑效果)
camera.speed *= camera_acc_speed
if camera.speed > camera_max_speed:
camera.speed = camera_max_speed
if camera.speed != 0:
dd = distance(camera.position, self.target_universe.planet.position)
camera.position = lerp(camera.position, self.target_universe.planet.position, camera.speed * time.dt / dd)
if camera.forward_speed != 0:
camera.position += camera.forward * camera.forward_speed
# if self.camera_target.planet.intersects(self.target_universe.planet).hit:
if camera.intersects(self.target_universe.planet).hit:
self.universe_reset()
self.update_text_panel()
def build_events(self):
# 订阅事件后,上面2个函数功能才会起作用
# 运行中,每时每刻都会触发 on_timer_changed
UrsinaEvent.on_timer_changed_subscription(wormhole_sim.on_timer_changed)
# 运行前会触发 on_ready
UrsinaEvent.on_ready_subscription(wormhole_sim.on_ready)
def run(self):
self.set_window_size((1920, 1079), False)
self.build_events()
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(self.bodies, SECONDS_PER_DAY * 24,
# position=(0, AU / 1200, -1.003 * AU),
# position=[-AU/100, 0, 0],
# position=[0, 0, -AU / 10],
# position=[0, 0, ],
position=[0, self.D, -self.D * 2],
cosmic_bg='',
gravity_works=False,
# bg_music='sounds/no_glory.mp3',
timer_enabled=True,
video_recoder=True,
show_exit_button=False,
show_grid=False,
show_camera_info=False,
show_control_info=False,
show_trail=False)
if __name__ == '__main__':
"""
"""
wormhole_sim = WormholeSim(texture_dir="cosmic_pan_02")
wormhole_sim.run()
...@@ -471,8 +471,8 @@ def create_line(from_pos, to_pos, parent=None, alpha=1.0, len_scale=1, set_light ...@@ -471,8 +471,8 @@ def create_line(from_pos, to_pos, parent=None, alpha=1.0, len_scale=1, set_light
return line return line
def create_circle(parent=None, pos=Vec3(0, 0, 0), thickness=1, scale=1, color=color.white, alpha=1): def create_circle(parent=None, pos=Vec3(0, 0, 0), thickness=1, scale=1, color=color.white, alpha=1, texture=None):
circle = Entity(parent=parent, model="circle", thickness=thickness, scale=scale, position=pos, circle = Entity(parent=parent, model="circle", texture=texture, thickness=thickness, scale=scale, position=pos,
color=color, alpha=alpha, double_sided=True) color=color, alpha=alpha, double_sided=True)
return circle return circle
......
...@@ -99,7 +99,7 @@ class UrsinaSimulator(Simulator): ...@@ -99,7 +99,7 @@ class UrsinaSimulator(Simulator):
c.enabled = False c.enabled = False
# Explosion animation # Explosion animation
def body_explode(target=None, scale=1, fps=6): def body_explode(target=None, scale=1, fps=6, explode_dir=None, explode_name="explosion"):
# from panda3d.core import GeomUtils # from panda3d.core import GeomUtils
if body.planet.enabled: if body.planet.enabled:
# TODO:下面代码保留,由于运行太快导致两个天体不是在表面碰撞,这样就要进行计算,希望在表面爆炸,但是需要耗费CPU资源,暂时注释 # TODO:下面代码保留,由于运行太快导致两个天体不是在表面碰撞,这样就要进行计算,希望在表面爆炸,但是需要耗费CPU资源,暂时注释
...@@ -120,8 +120,14 @@ class UrsinaSimulator(Simulator): ...@@ -120,8 +120,14 @@ class UrsinaSimulator(Simulator):
# 如果爆炸,则静止不动(停止并忽略引力) # 如果爆炸,则静止不动(停止并忽略引力)
body.stop_and_ignore_gravity() body.stop_and_ignore_gravity()
body.planet.enabled = False body.planet.enabled = False
explosion_file = find_file("images/explosion") if explode_dir is None:
explosion_file = os.path.join(explosion_file, "explosion") explode_dir = "images/explosion"
explosion_file = find_file(explode_dir)
if not os.path.exists(explosion_file):
return
explosion_file = os.path.join(explosion_file, explode_name)
# 获取体积数据(开三次方) # 获取体积数据(开三次方)
volume_scale = pow(body.planet.model.get_bounds().volume, 1 / 3) volume_scale = pow(body.planet.model.get_bounds().volume, 1 / 3)
# 根据体积、大小缩放判断爆炸的量 # 根据体积、大小缩放判断爆炸的量
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册