426 lines
15 KiB
Python
426 lines
15 KiB
Python
import os
|
|
from pathlib import Path
|
|
from core.editor_context import get_editor_context
|
|
|
|
|
|
class RuntimeActions:
|
|
"""Runtime-facing creation and utility actions extracted from main world."""
|
|
|
|
def __init__(self, app):
|
|
self.app = app
|
|
object.__setattr__(self, "_editor_context", get_editor_context(app))
|
|
|
|
def __getattr__(self, name):
|
|
return getattr(self.app, name)
|
|
|
|
def __setattr__(self, name, value):
|
|
if name == "app" or name.startswith("_") or name in self.__dict__ or hasattr(type(self), name):
|
|
object.__setattr__(self, name, value)
|
|
else:
|
|
setattr(self.app, name, value)
|
|
|
|
def _get_editor_context(self):
|
|
context = getattr(self, "_editor_context", None)
|
|
if context is None:
|
|
context = get_editor_context(self.app)
|
|
object.__setattr__(self, "_editor_context", context)
|
|
return context
|
|
|
|
def _get_gui_manager(self):
|
|
"""统一获取 GUI 管理器,避免散落的 hasattr 判空。"""
|
|
return self._get_editor_context().get_gui_manager()
|
|
|
|
def _append_gui_element(self, element_wrapper):
|
|
"""统一维护 gui_elements 列表。"""
|
|
gui_manager = self._get_gui_manager()
|
|
if not gui_manager:
|
|
return False
|
|
self._get_editor_context().append_gui_element(element_wrapper)
|
|
return True
|
|
|
|
def createGUIButton(self, pos=(0, 0, 0), text="按钮", size=0.1):
|
|
"""创建2D GUI按钮"""
|
|
try:
|
|
if self._get_gui_manager():
|
|
return self._create_simple_gui_button(pos, text, size)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建GUI按钮失败: {e}")
|
|
return None
|
|
|
|
def _create_simple_gui_button(self, pos=(0, 0, 0), text="按钮", size=0.1):
|
|
"""创建简单的GUI按钮。"""
|
|
try:
|
|
from direct.gui.DirectGui import DirectButton
|
|
|
|
gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
|
|
font = self._get_chinese_font()
|
|
|
|
if isinstance(size, (list, tuple)) and len(size) >= 2:
|
|
scale_value = size[0]
|
|
else:
|
|
scale_value = size
|
|
|
|
button = DirectButton(
|
|
text=text,
|
|
pos=gui_pos,
|
|
scale=scale_value,
|
|
text_font=font,
|
|
command=self._on_gui_button_click
|
|
)
|
|
|
|
button_wrapper = type("GUIElement", (), {})()
|
|
button_wrapper.node = button
|
|
button_wrapper.name = text
|
|
button_wrapper.gui_type = "GUI_BUTTON"
|
|
button_wrapper.position = pos
|
|
button_wrapper.size = size
|
|
|
|
if not self._append_gui_element(button_wrapper):
|
|
return None
|
|
|
|
print(f"✓ GUI按钮创建成功: {text}")
|
|
return button_wrapper
|
|
except Exception as e:
|
|
print(f"✗ 创建简单GUI按钮失败: {e}")
|
|
return None
|
|
|
|
def _on_gui_button_click(self):
|
|
"""GUI按钮点击事件处理"""
|
|
print("GUI按钮被点击了")
|
|
|
|
def _toggle_vr_mode(self):
|
|
"""切换VR模式"""
|
|
if self.vr_manager:
|
|
if self.vr_manager.is_enabled():
|
|
self._exit_vr_mode()
|
|
else:
|
|
self.vr_manager.enable()
|
|
self.add_info_message("已进入VR模式")
|
|
else:
|
|
self.add_error_message("VR管理器未初始化")
|
|
|
|
def _exit_vr_mode(self):
|
|
"""退出VR模式"""
|
|
if self.vr_manager:
|
|
self.vr_manager.disable()
|
|
self.add_info_message("已退出VR模式")
|
|
|
|
def _show_vr_status(self):
|
|
"""显示VR状态"""
|
|
if self.vr_manager:
|
|
status = "已启用" if self.vr_manager.is_enabled() else "未启用"
|
|
self.add_info_message(f"VR状态: {status}")
|
|
|
|
if self.vr_manager.is_enabled():
|
|
devices = self.vr_manager.get_connected_devices()
|
|
if devices:
|
|
self.add_info_message(f"连接的设备: {', '.join(devices)}")
|
|
else:
|
|
self.add_info_message("未检测到VR设备")
|
|
else:
|
|
self.add_error_message("VR管理器未初始化")
|
|
|
|
def _show_vr_settings(self):
|
|
"""显示VR设置"""
|
|
if self.vr_manager:
|
|
self.add_info_message("VR设置对话框待实现")
|
|
else:
|
|
self.add_error_message("VR管理器未初始化")
|
|
|
|
def _show_vr_performance_report(self):
|
|
"""显示VR性能报告"""
|
|
if self.vr_manager and self.vr_manager.is_enabled():
|
|
report = self.vr_manager.get_performance_report()
|
|
self.add_info_message(f"VR性能报告: {report}")
|
|
else:
|
|
self.add_info_message("VR未启用或管理器未初始化")
|
|
|
|
def _get_chinese_font(self):
|
|
"""获取中文字体"""
|
|
try:
|
|
from panda3d.core import TextNode
|
|
|
|
font_paths = [
|
|
"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
|
|
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
|
|
"/System/Library/Fonts/PingFang.ttc",
|
|
"C:/Windows/Fonts/simhei.ttf",
|
|
"C:/Windows/Fonts/msyh.ttc",
|
|
]
|
|
|
|
for font_path in font_paths:
|
|
if Path(font_path).exists():
|
|
try:
|
|
font = self.loader.loadFont(font_path)
|
|
print(f"✓ 为GUI加载中文字体成功: {font_path}")
|
|
return font
|
|
except Exception:
|
|
print(f"⚠️ 字体加载失败,尝试下一个: {font_path}")
|
|
continue
|
|
|
|
print("⚠️ 无法加载中文字体,使用默认字体")
|
|
return TextNode.getDefaultFont()
|
|
|
|
except Exception as e:
|
|
print(f"⚠️ 获取中文字体失败: {e}")
|
|
from panda3d.core import TextNode
|
|
return TextNode.getDefaultFont()
|
|
|
|
def createGUILabel(self, pos=(0, 0, 0), text="标签", size=0.08):
|
|
"""创建2D GUI标签"""
|
|
try:
|
|
if self._get_gui_manager():
|
|
return self._create_simple_gui_label(pos, text, size)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建GUI标签失败: {e}")
|
|
return None
|
|
|
|
def _create_simple_gui_label(self, pos=(0, 0, 0), text="标签", size=0.08):
|
|
"""创建简单的GUI标签。"""
|
|
try:
|
|
from direct.gui.DirectGui import DirectLabel
|
|
|
|
gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
|
|
font = self._get_chinese_font()
|
|
|
|
if isinstance(size, (list, tuple)) and len(size) >= 2:
|
|
scale_value = size[0]
|
|
else:
|
|
scale_value = size
|
|
|
|
label = DirectLabel(
|
|
text=text,
|
|
pos=gui_pos,
|
|
scale=scale_value,
|
|
text_font=font,
|
|
text_fg=(1, 1, 1, 1)
|
|
)
|
|
|
|
label_wrapper = type("GUIElement", (), {})()
|
|
label_wrapper.node = label
|
|
label_wrapper.name = text
|
|
label_wrapper.gui_type = "GUI_LABEL"
|
|
label_wrapper.position = pos
|
|
label_wrapper.size = size
|
|
|
|
if not self._append_gui_element(label_wrapper):
|
|
return None
|
|
|
|
print(f"✓ GUI标签创建成功: {text}")
|
|
return label_wrapper
|
|
except Exception as e:
|
|
print(f"✗ 创建简单GUI标签失败: {e}")
|
|
return None
|
|
|
|
def createGUIEntry(self, pos=(0, 0, 0), placeholder="输入文本...", size=0.08):
|
|
"""创建2D GUI文本输入框"""
|
|
try:
|
|
if self._get_gui_manager():
|
|
return self._create_simple_gui_entry(pos, placeholder, size)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建GUI输入框失败: {e}")
|
|
return None
|
|
|
|
def _create_simple_gui_entry(self, pos=(0, 0, 0), placeholder="输入文本...", size=0.08):
|
|
"""创建简单的GUI输入框。"""
|
|
try:
|
|
from direct.gui.DirectGui import DirectEntry
|
|
|
|
gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
|
|
font = self._get_chinese_font()
|
|
|
|
if isinstance(size, (list, tuple)) and len(size) >= 2:
|
|
scale_value = size[0]
|
|
else:
|
|
scale_value = size
|
|
|
|
entry = DirectEntry(
|
|
text=placeholder,
|
|
pos=gui_pos,
|
|
scale=scale_value,
|
|
text_font=font,
|
|
width=20,
|
|
numLines=1,
|
|
focus=1
|
|
)
|
|
|
|
entry_wrapper = type("GUIElement", (), {})()
|
|
entry_wrapper.node = entry
|
|
entry_wrapper.name = placeholder
|
|
entry_wrapper.gui_type = "GUI_ENTRY"
|
|
entry_wrapper.position = pos
|
|
entry_wrapper.size = size
|
|
|
|
if not self._append_gui_element(entry_wrapper):
|
|
return None
|
|
|
|
print(f"✓ GUI输入框创建成功: {placeholder}")
|
|
return entry_wrapper
|
|
except Exception as e:
|
|
print(f"✗ 创建简单GUI输入框失败: {e}")
|
|
return None
|
|
|
|
def createGUIImage(self, pos=(0, 0, 0), image_path=None, size=2):
|
|
"""创建2D GUI图片"""
|
|
try:
|
|
if self._get_gui_manager():
|
|
return self._create_simple_gui_image(pos, image_path, size)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建GUI图片失败: {e}")
|
|
return None
|
|
|
|
def _create_simple_gui_image(self, pos=(0, 0, 0), image_path=None, size=2):
|
|
"""创建简单的GUI图片。"""
|
|
try:
|
|
from direct.gui.DirectGui import DirectFrame
|
|
from panda3d.core import Filename
|
|
|
|
gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
|
|
|
|
if isinstance(size, (list, tuple)) and len(size) >= 2:
|
|
scale_value = size[0]
|
|
else:
|
|
scale_value = size
|
|
|
|
if image_path and os.path.exists(image_path):
|
|
tex = self.loader.loadTexture(Filename.fromOsSpecific(image_path))
|
|
image = DirectFrame(
|
|
image=tex,
|
|
pos=gui_pos,
|
|
scale=scale_value
|
|
)
|
|
image_name = os.path.basename(image_path)
|
|
else:
|
|
image = DirectFrame(
|
|
frameColor=(0.5, 0.5, 0.5, 1.0),
|
|
frameSize=(-scale_value, scale_value, -scale_value, scale_value),
|
|
pos=gui_pos
|
|
)
|
|
image_name = "占位符图片"
|
|
|
|
image_wrapper = type("GUIElement", (), {})()
|
|
image_wrapper.node = image
|
|
image_wrapper.name = image_name
|
|
image_wrapper.gui_type = "GUI_IMAGE"
|
|
image_wrapper.position = pos
|
|
image_wrapper.size = size
|
|
image_wrapper.image_path = image_path
|
|
|
|
if not self._append_gui_element(image_wrapper):
|
|
return None
|
|
|
|
print(f"✓ GUI图片创建成功: {image_name}")
|
|
return image_wrapper
|
|
except Exception as e:
|
|
print(f"✗ 创建简单GUI图片失败: {e}")
|
|
return None
|
|
|
|
def createVideoScreen(self, pos=(0, 0, 0), size=1, video_path=None):
|
|
"""创建视频屏幕"""
|
|
try:
|
|
gui_manager = self._get_gui_manager()
|
|
if gui_manager:
|
|
return gui_manager.createVideoScreen(pos, size, video_path)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建视频屏幕失败: {e}")
|
|
return None
|
|
|
|
def create2DVideoScreen(self, pos=(0, 0, 0), size=0.2, video_path=None):
|
|
"""创建2D视频屏幕"""
|
|
try:
|
|
gui_manager = self._get_gui_manager()
|
|
if gui_manager:
|
|
return gui_manager.createGUI2DVideoScreen(pos, size, video_path)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建2D视频屏幕失败: {e}")
|
|
return None
|
|
|
|
def createSphericalVideo(self, pos=(0, 0, 0), radius=5.0, video_path=None):
|
|
"""创建360度视频"""
|
|
try:
|
|
gui_manager = self._get_gui_manager()
|
|
if gui_manager:
|
|
return gui_manager.createSphericalVideo(pos, radius, video_path)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建球形视频失败: {e}")
|
|
return None
|
|
|
|
def createVirtualScreen(self, pos=(0, 0, 0), size=(2, 1), text="虚拟屏幕"):
|
|
"""创建虚拟屏幕"""
|
|
try:
|
|
gui_manager = self._get_gui_manager()
|
|
if gui_manager:
|
|
return gui_manager.createGUIVirtualScreen(pos, size, text)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建虚拟屏幕失败: {e}")
|
|
return None
|
|
|
|
def createSpotLight(self, pos=(0, 0, 5)):
|
|
"""创建聚光灯"""
|
|
try:
|
|
if hasattr(self, "scene_manager") and self.scene_manager:
|
|
return self.scene_manager.createSpotLight(pos)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建聚光灯失败: {e}")
|
|
return None
|
|
|
|
def createPointLight(self, pos=(0, 0, 5)):
|
|
"""创建点光源"""
|
|
try:
|
|
if hasattr(self, "scene_manager") and self.scene_manager:
|
|
return self.scene_manager.createPointLight(pos)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建点光源失败: {e}")
|
|
return None
|
|
|
|
def createFlatTerrain(self, size=(10, 10), resolution=129):
|
|
"""创建平面地形"""
|
|
try:
|
|
if hasattr(self, "terrain_manager") and self.terrain_manager:
|
|
return self.terrain_manager.createFlatTerrain(size, resolution)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建平面地形失败: {e}")
|
|
return None
|
|
|
|
def createTerrainFromHeightMap(self, heightmap_path, scale=(1.0, 1.0, 10.0)):
|
|
"""从高度图创建地形"""
|
|
try:
|
|
if hasattr(self, "terrain_manager") and self.terrain_manager:
|
|
return self.terrain_manager.createTerrainFromHeightMap(heightmap_path, scale)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建高度图地形失败: {e}")
|
|
return None
|
|
|
|
def createScript(self, script_name, template="basic"):
|
|
"""创建脚本"""
|
|
try:
|
|
if hasattr(self, "script_manager") and self.script_manager:
|
|
return self.script_manager.createScript(script_name, template)
|
|
return None
|
|
except Exception as e:
|
|
print(f"创建脚本失败: {e}")
|
|
return None
|
|
|
|
def loadScript(self, script_path):
|
|
"""加载脚本"""
|
|
try:
|
|
if hasattr(self, "script_manager") and self.script_manager:
|
|
return self.script_manager.loadScript(script_path)
|
|
return None
|
|
except Exception as e:
|
|
print(f"加载脚本失败: {e}")
|
|
return None
|
|
|