封装
This commit is contained in:
parent
bab5a9bb27
commit
26faa11ae0
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -1,4 +1,4 @@
|
||||
{
|
||||
"python-envs.pythonProjects": [],
|
||||
"kiroAgent.configureMCP": "Disabled"
|
||||
"kiroAgent.configureMCP": "Disabled",
|
||||
"python-envs.defaultEnvManager": "ms-python.python:pyenv"
|
||||
}
|
||||
218
AGENTS.md
218
AGENTS.md
@ -1,218 +0,0 @@
|
||||
# EG 项目概览与开发指南
|
||||
|
||||
## 项目简介
|
||||
|
||||
EG 是一个基于 Panda3D 引擎开发的 3D 编辑器和游戏引擎,集成了高级渲染管线、VR 支持、物理模拟、脚本系统等功能。该项目主要用于创建 3D 场景、游戏开发和交互式应用程序。
|
||||
|
||||
## 核心技术栈
|
||||
|
||||
- **渲染引擎**: Panda3D 1.10.15 + RenderPipeline (延迟渲染、PBR材质)
|
||||
- **GUI框架**: imgui_bundle + p3dimgui (用于编辑器界面)
|
||||
- **VR支持**: OpenVR 2.2.0
|
||||
- **脚本系统**: Python 3.11
|
||||
- **其他依赖**: openvr, numpy, aiohttp, pyassimp, pillow 等
|
||||
|
||||
## 项目架构
|
||||
|
||||
### 核心模块
|
||||
|
||||
- **main.py** - 应用程序入口点,初始化所有系统
|
||||
- **Start_Run.py** - 启动脚本,设置环境变量和路径
|
||||
- **core/** - 核心功能模块
|
||||
- `world.py` - 世界管理器,处理场景渲染和更新
|
||||
- `selection.py` - 选择系统,处理对象选择
|
||||
- `event_handler.py` - 事件处理系统
|
||||
- `tool_manager.py` - 工具管理器
|
||||
- `script_system.py` - 脚本系统
|
||||
- `patrol_system.py` - 巡逻系统
|
||||
- `Command_System.py` - 命令系统
|
||||
- `terrain_manager.py` - 地形管理
|
||||
- `vr_manager.py` - VR 功能管理
|
||||
- `collision_manager.py` - 碰撞检测管理
|
||||
- `resource_manager.py` - 资源管理
|
||||
- `assembly_interaction.py` - 装配交互
|
||||
- `maintenance_gui.py` - 维护界面
|
||||
- `render_pipeline_utils.py` - 渲染管线工具
|
||||
- `InfoPanelManager.py` - 信息面板管理
|
||||
- `CustomMouseController.py` - 自定义鼠标控制器
|
||||
|
||||
### GUI 系统
|
||||
|
||||
- **gui/gui_manager.py** - GUI管理器,处理2D/3D界面元素
|
||||
- **ui/icon_manager.py** - 图标管理器
|
||||
|
||||
### 场景管理
|
||||
|
||||
- **scene/scene_manager.py** - 场景管理器,处理模型导入、场景树构建
|
||||
- **scene/util.py** - 场景工具函数
|
||||
|
||||
### 项目管理
|
||||
|
||||
- **project/project_manager.py** - 项目管理器,处理项目创建、保存、加载
|
||||
|
||||
### 脚本系统
|
||||
|
||||
- **scripts/** - 包含各种预定义脚本
|
||||
- `MoverScript.py` - 移动脚本
|
||||
- `RotatorScript.py` - 旋转脚本
|
||||
- `ScalerScript.py` - 缩放脚本
|
||||
- `ColorChangerScript.py` - 颜色变化脚本
|
||||
- `FollowerScript.py` - 跟随脚本
|
||||
- `BouncerScript.py` - 弹跳脚本
|
||||
- `ComboAnimatorScript.py` - 组合动画脚本
|
||||
|
||||
### VR 系统
|
||||
|
||||
- **vr_actions/** - VR动作配置
|
||||
- `actions.json` - VR动作定义
|
||||
- `bindings_*.json` - 不同VR设备的绑定配置
|
||||
|
||||
### 资源管理
|
||||
|
||||
- **Resources/** - 资源目录
|
||||
- `models/` - 3D模型
|
||||
- `textures/` - 纹理贴图
|
||||
- `materials/` - 材质文件
|
||||
- `animations/` - 动画文件
|
||||
- `icons/` - 图标资源
|
||||
|
||||
### 渲染管线
|
||||
|
||||
- **RenderPipelineFile/** - 高级渲染管线
|
||||
- `rpcore/` - 渲染管线核心
|
||||
- `rpplugins/` - 渲染插件
|
||||
- `effects/` - 后处理效果
|
||||
- `config/` - 渲染配置
|
||||
|
||||
## 启动和运行
|
||||
|
||||
### 环境要求
|
||||
|
||||
- Python 3.11
|
||||
- Panda3D 1.10.15
|
||||
- OpenVR 2.2.0 (VR功能)
|
||||
|
||||
### 运行方式
|
||||
|
||||
1. **直接运行主程序**:
|
||||
```bash
|
||||
python Start_Run.py
|
||||
```
|
||||
|
||||
2. **带项目路径运行**:
|
||||
```bash
|
||||
python Start_Run.py /path/to/project
|
||||
```
|
||||
|
||||
3. **从main.py运行**:
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
### 配置文件
|
||||
|
||||
- **config/vr_settings.json** - VR渲染配置
|
||||
- **imgui.ini** - ImGui界面配置
|
||||
- **.gitignore** - Git忽略文件配置
|
||||
|
||||
## 开发约定
|
||||
|
||||
### 代码风格
|
||||
|
||||
- 使用UTF-8编码
|
||||
- 遵循PEP 8代码规范
|
||||
- 类名使用驼峰命名法
|
||||
- 函数和变量使用下划线命名法
|
||||
- 文件头部包含模块说明和导入信息
|
||||
|
||||
### 脚本开发
|
||||
|
||||
- 所有用户脚本应继承 `ScriptBase` 类
|
||||
- 脚本文件放在 `scripts/` 目录下
|
||||
- 使用 `ScriptManager` 管理脚本生命周期
|
||||
- 脚本API通过 `world` 对象提供
|
||||
|
||||
### 插件开发
|
||||
|
||||
- 插件系统支持动态加载
|
||||
- 插件配置文件使用JSON格式
|
||||
- 插件应实现标准接口
|
||||
|
||||
### VR开发
|
||||
|
||||
- VR动作配置在 `vr_actions/actions.json` 中定义
|
||||
- 支持多种VR设备(Vive、Oculus、Index)
|
||||
- VR渲染配置在 `config/vr_settings.json` 中
|
||||
|
||||
## 构建和部署
|
||||
|
||||
### 依赖安装
|
||||
|
||||
```bash
|
||||
pip install -r requirements/requirements.txt
|
||||
```
|
||||
|
||||
### 测试
|
||||
|
||||
项目包含多个测试脚本:
|
||||
- `test_quick_script.py` - 快速测试脚本
|
||||
- `TestMover.py` - 移动测试
|
||||
- `TestRotator.py` - 旋转测试
|
||||
- `TestScaler.py` - 缩放测试
|
||||
|
||||
### 项目文件
|
||||
|
||||
- 项目配置使用JSON格式
|
||||
- 项目文件包含场景、资源、脚本等信息
|
||||
- 使用 `ProjectManager` 管理项目生命周期
|
||||
|
||||
## 常见问题
|
||||
|
||||
### VR相关问题
|
||||
|
||||
1. 确保VR设备已正确连接
|
||||
2. 检查OpenVR运行时是否安装
|
||||
3. 验证VR动作配置是否正确
|
||||
|
||||
### 渲染问题
|
||||
|
||||
1. 检查显卡驱动是否最新
|
||||
2. 确认RenderPipeline配置正确
|
||||
3. 验证材质和纹理路径
|
||||
|
||||
### 性能优化
|
||||
|
||||
1. 使用合适的LOD设置
|
||||
2. 优化场景复杂度
|
||||
3. 调整渲染质量设置
|
||||
|
||||
## 扩展开发
|
||||
|
||||
### 添加新脚本
|
||||
|
||||
1. 在 `scripts/` 目录创建新脚本文件
|
||||
2. 继承 `ScriptBase` 类
|
||||
3. 实现必要的方法
|
||||
4. 通过 `ScriptManager` 注册脚本
|
||||
|
||||
### 添加新工具
|
||||
|
||||
1. 在 `core/tool_manager.py` 中注册新工具
|
||||
2. 实现工具逻辑
|
||||
3. 添加GUI界面元素
|
||||
|
||||
### 添加新渲染效果
|
||||
|
||||
1. 在 `RenderPipelineFile/rpplugins/` 目录创建插件
|
||||
2. 实现渲染逻辑
|
||||
3. 添加配置选项
|
||||
|
||||
## 联系和支持
|
||||
|
||||
- 项目Git仓库: http://10.0.0.99:4000/Rowland/EG.git
|
||||
- 当前分支: imgui
|
||||
- 最新提交: 移除qt依赖 (33e62bd1e4c2c8d3aac15e045b419edb8992d7ff)
|
||||
|
||||
---
|
||||
|
||||
*该文档由iFlow CLI自动生成,最后更新时间: 2026年3月17日*
|
||||
BIN
Assets/Models/jyc.glb
Normal file
BIN
Assets/Models/jyc.glb
Normal file
Binary file not shown.
8
Assets/Models/jyc.glb.meta
Normal file
8
Assets/Models/jyc.glb.meta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"guid": "b547e6c1347a4ec0bb913a9cb1a7ec6b",
|
||||
"asset_type": "model",
|
||||
"source_hash": "46fc525a88f6d16eed0bac714a14692b68818d36a00065690b5877d878788948",
|
||||
"importer": "model_importer",
|
||||
"import_settings": {},
|
||||
"dependency_guids": []
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
# EG 本地项目模块分析(Qt 清理后)
|
||||
|
||||
更新时间:2026-02-28
|
||||
分析范围:`d:\IMGUI\EG` 本地文件,不包含任何远程仓库信息。
|
||||
|
||||
## 1. 当前结论
|
||||
|
||||
- 主编辑器路径已切换为 ImGui 方案,Qt/PySide 在项目代码主路径中已清理完成。
|
||||
- 本地扫描结果(排除 `RenderPipelineFile` 第三方目录):
|
||||
- `PyQt/PySide/Qt` 关键引用:`0`
|
||||
- Python 脚本数:`385`
|
||||
- 当前主要技术债已从“Qt 运行时依赖”转为“旧接口命名和耦合残留”。
|
||||
- 已完成基础稳定性优化:入口保护、兼容字段初始化、脚本恢复拼写错误修复。
|
||||
- 已完成第一轮接口收敛:`event_handler/selection/runtime_actions/InfoPanelManager` 已改为 helper 访问模式。
|
||||
|
||||
## 2. 本轮 Qt 清理落点
|
||||
|
||||
已完成清理的关键文件:
|
||||
|
||||
- [core/InfoPanelManager.py](d:/IMGUI/EG/core/InfoPanelManager.py)
|
||||
- [core/selection.py](d:/IMGUI/EG/core/selection.py)
|
||||
- [scene/scene_manager_convert_tiles_mixin.py](d:/IMGUI/EG/scene/scene_manager_convert_tiles_mixin.py)
|
||||
- [ui/widgets.py](d:/IMGUI/EG/ui/widgets.py)
|
||||
- [ui/icon_manager.py](d:/IMGUI/EG/ui/icon_manager.py)
|
||||
- [core/world.py](d:/IMGUI/EG/core/world.py)
|
||||
- [core/vr_manager.py](d:/IMGUI/EG/core/vr_manager.py)
|
||||
- [core/vr/testing/test_mode.py](d:/IMGUI/EG/core/vr/testing/test_mode.py)
|
||||
- [scene/tree_roles.py](d:/IMGUI/EG/scene/tree_roles.py)
|
||||
- [TransformGizmo/move_gizmo.py](d:/IMGUI/EG/TransformGizmo/move_gizmo.py)
|
||||
- [TransformGizmo/rotate_gizmo.py](d:/IMGUI/EG/TransformGizmo/rotate_gizmo.py)
|
||||
- [TransformGizmo/scale_gizmo.py](d:/IMGUI/EG/TransformGizmo/scale_gizmo.py)
|
||||
- [requirements/requirements.txt](d:/IMGUI/EG/requirements/requirements.txt)
|
||||
- [requirements/clean-requirements.txt](d:/IMGUI/EG/requirements/clean-requirements.txt)
|
||||
- [requirements/environment.yml](d:/IMGUI/EG/requirements/environment.yml)
|
||||
- [requirements/DEPLOYMENT_README.md](d:/IMGUI/EG/requirements/DEPLOYMENT_README.md)
|
||||
|
||||
## 3. 仍需优化的结构点(非 Qt 依赖问题)
|
||||
|
||||
- 旧接口命名残留仍较多:`interface_manager/treeWidget/gui_manager` 引用约 `92` 处。
|
||||
- `main.py` 仍是脚本式入口(模块导入副作用风险仍在)。
|
||||
- 场景树与 GUI 元素生命周期在多个模块分散维护,后续建议继续做单一上下文收敛。
|
||||
|
||||
## 4. 风险分级(Qt 清理后)
|
||||
|
||||
- 高风险:
|
||||
- 多处旧接口命名仍在调用链中,后续重构若不做适配层可能引发行为回归。
|
||||
- 中风险:
|
||||
- 场景保存/加载与树状态同步仍跨模块分散,维护成本高。
|
||||
- 低风险:
|
||||
- 文档和模板文件中仍有历史术语,易造成新成员理解偏差。
|
||||
|
||||
## 5. 后续建议顺序
|
||||
|
||||
1. 建立统一 EditorContext,收敛 `interface_manager/gui_manager` 访问入口。
|
||||
2. 将场景树操作抽象为 ImGui 专用接口,逐步移除 `treeWidget` 语义。
|
||||
3. 统一入口规范(`if __name__ == "__main__":`)并补最小回归脚本。
|
||||
|
||||
## 6. 全量目录与脚本清单
|
||||
|
||||
为后续优化分析,完整目录树与全部脚本行数/用途清单见:
|
||||
|
||||
- [PROJECT_FULL_CATALOG.md](d:/IMGUI/EG/PROJECT_FULL_CATALOG.md)
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,352 +0,0 @@
|
||||
# PROJECT_MODULE_INDEX
|
||||
|
||||
> 本文档只基于本地工作区扫描生成,不依赖 Git 远程信息。
|
||||
|
||||
- 生成时间: 2026-02-28 16:41:46
|
||||
- 目标: 为后续优化/重构提供 功能模块 -> 文件 的快速定位索引
|
||||
|
||||
## 0. 关联分析文档
|
||||
|
||||
- 优化分析(本轮): `PROJECT_OPTIMIZATION_ANALYSIS.md`
|
||||
- Qt 清理迁移清单: `QT_TO_IMGUI_MIGRATION_CHECKLIST.md`
|
||||
- 历史模块分析: `IMGUI_MODULE_ANALYSIS.md`
|
||||
|
||||
## 1. 一级目录规模(Python)
|
||||
|
||||
| 目录 | 文件数 | 代码行数 |
|
||||
|---|---:|---:|
|
||||
| core | 40 | 19892 |
|
||||
| gui | 1 | 7 |
|
||||
| project | 2 | 759 |
|
||||
| root | 2 | 812 |
|
||||
| scene | 10 | 3792 |
|
||||
| scripts | 18 | 1378 |
|
||||
| ssbo_component | 3 | 1907 |
|
||||
| templates | 1 | 1282 |
|
||||
| tools | 1 | 73 |
|
||||
| TransformGizmo | 5 | 3705 |
|
||||
| ui | 63 | 17403 |
|
||||
|
||||
## 2. 功能模块 -> 关键文件映射
|
||||
|
||||
### 应用启动与组装
|
||||
|
||||
- 模块职责: 程序入口、系统初始化、模块装配
|
||||
- 对应文件:
|
||||
- Start_Run.py
|
||||
- main.py
|
||||
- templates/main_template.py
|
||||
|
||||
### 世界与运行时核心
|
||||
|
||||
- 模块职责: 世界生命周期、输入事件、选择、命令、资源、碰撞、地形等
|
||||
- 对应文件:
|
||||
- core/world.py
|
||||
- core/event_handler.py
|
||||
- core/selection.py
|
||||
- core/Command_System.py
|
||||
- core/tool_manager.py
|
||||
- core/script_system.py
|
||||
- core/resource_manager.py
|
||||
- core/collision_manager.py
|
||||
- core/terrain_manager.py
|
||||
- core/model_drag_drop.py
|
||||
- core/InfoPanelManager.py
|
||||
- core/CustomMouseController.py
|
||||
- core/imgui_style_manager.py
|
||||
- core/imgui_webview.py
|
||||
- core/selection_outline.py
|
||||
- core/render_pipeline_utils.py
|
||||
|
||||
### VR 系统
|
||||
|
||||
- 模块职责: VR 管理、渲染阶段、交互、追踪、性能、可视化、配置
|
||||
- 对应文件:
|
||||
- core/vr_manager.py
|
||||
- core/vr/config/vr_config.py
|
||||
- core/vr/config/joystick_config.py
|
||||
- core/vr/config/shadow_stage.py
|
||||
- core/vr/rendering/stages.py
|
||||
- core/vr/interaction/actions.py
|
||||
- core/vr/interaction/grab.py
|
||||
- core/vr/interaction/joystick.py
|
||||
- core/vr/interaction/teleport.py
|
||||
- core/vr/tracking/controllers.py
|
||||
- core/vr/visualization/controllers.py
|
||||
- core/vr/visualization/effects.py
|
||||
- core/vr/performance/monitoring.py
|
||||
- core/vr/performance/optimization.py
|
||||
- core/vr/testing/test_mode.py
|
||||
|
||||
### 场景管理
|
||||
|
||||
- 模块职责: 模型导入、灯光管理、序列化、IO、切片转换
|
||||
- 对应文件:
|
||||
- scene/scene_manager.py
|
||||
- scene/scene_manager_impl.py
|
||||
- scene/scene_manager_model_mixin.py
|
||||
- scene/scene_manager_light_mixin.py
|
||||
- scene/scene_manager_serialization_mixin.py
|
||||
- scene/scene_manager_io_mixin.py
|
||||
- scene/scene_manager_convert_tiles_mixin.py
|
||||
- scene/util.py
|
||||
- scene/tree_roles.py
|
||||
|
||||
### 项目管理
|
||||
|
||||
- 模块职责: 项目创建、打开、保存、资源清单
|
||||
- 对应文件:
|
||||
- project/project_manager.py
|
||||
|
||||
### 编辑器面板 (ImGui)
|
||||
|
||||
- 模块职责: 顶部/左侧/中心/右侧属性面板、弹窗、运行时动作、创建对象、动画工具
|
||||
- 对应文件:
|
||||
- ui/panels/editor_panels.py
|
||||
- ui/panels/editor_panels_top.py
|
||||
- ui/panels/editor_panels_left.py
|
||||
- ui/panels/editor_panels_center.py
|
||||
- ui/panels/editor_panels_right.py
|
||||
- ui/panels/editor_panels_right_transform.py
|
||||
- ui/panels/editor_panels_right_material.py
|
||||
- ui/panels/editor_panels_right_collision.py
|
||||
- ui/panels/panel_delegates.py
|
||||
- ui/panels/property_helpers.py
|
||||
- ui/panels/runtime_actions.py
|
||||
- ui/panels/create_actions.py
|
||||
- ui/panels/app_actions.py
|
||||
- ui/panels/dialog_panels.py
|
||||
- ui/panels/script_panels.py
|
||||
- ui/panels/interaction_panels.py
|
||||
- ui/panels/object_factory.py
|
||||
- ui/panels/animation_tools.py
|
||||
|
||||
### LUI 编辑器与组件
|
||||
|
||||
- 模块职责: LUI 管理、属性编辑、交互编辑、组件定义
|
||||
- 对应文件:
|
||||
- ui/lui_manager.py
|
||||
- ui/lui_function.py
|
||||
- ui/LUI/lui_manager_editor.py
|
||||
- ui/LUI/lui_manager_interaction.py
|
||||
- ui/LUI/lui_function_properties.py
|
||||
- ui/LUI/lui_function_components.py
|
||||
- ui/LUI/lui_shared.py
|
||||
- ui/widgets.py
|
||||
- ui/icon_manager.py
|
||||
|
||||
### LUI 内建控件与皮肤
|
||||
|
||||
- 模块职责: LUI 基础控件实现与皮肤资源脚本
|
||||
- 对应文件:
|
||||
- ui/Builtin/Elements.py
|
||||
- ui/Builtin/LUI*.py
|
||||
- ui/Builtin/RectTransform.py
|
||||
- ui/Skins/Metro/LUIMetroSkin.py
|
||||
- ui/Skins/Metro/copy_frames.py
|
||||
|
||||
### Transform Gizmo
|
||||
|
||||
- 模块职责: 移动/旋转/缩放 gizmo 与事件
|
||||
- 对应文件:
|
||||
- TransformGizmo/transform_gizmo.py
|
||||
- TransformGizmo/move_gizmo.py
|
||||
- TransformGizmo/rotate_gizmo.py
|
||||
- TransformGizmo/scale_gizmo.py
|
||||
- TransformGizmo/events.py
|
||||
|
||||
### SSBO 选取与编辑
|
||||
|
||||
- 模块职责: 基于 SSBO 的对象选取与编辑器
|
||||
- 对应文件:
|
||||
- ssbo_component/ssbo_editor.py
|
||||
- ssbo_component/ssbo_controller.py
|
||||
- ssbo_component/demo_component.py
|
||||
|
||||
### 脚本样例与测试
|
||||
|
||||
- 模块职责: 内置脚本、旋转/移动/缩放测试脚本
|
||||
- 对应文件:
|
||||
- scripts/*.py
|
||||
|
||||
### 开发工具
|
||||
|
||||
- 模块职责: 辅助分析脚本
|
||||
- 对应文件:
|
||||
- tools/open_source_rate.py
|
||||
|
||||
## 3. 配置与运行关键文件
|
||||
|
||||
- config/vr_settings.json
|
||||
- core/vr/config/vr_settings.json
|
||||
- vr_actions/actions.json
|
||||
- vr_actions/bindings_index.json
|
||||
- vr_actions/bindings_oculus.json
|
||||
- vr_actions/bindings_vive.json
|
||||
- imgui.ini
|
||||
- requirements/requirements.txt
|
||||
- requirements/clean-requirements.txt
|
||||
- requirements/conda-requirements.txt
|
||||
- requirements/environment.yml
|
||||
|
||||
## 4. 第三方/外部子树说明
|
||||
|
||||
- RenderPipelineFile/: 第三方渲染管线源码与工具,Python 文件 246 个,约 31629 行。
|
||||
- 优化建议: 优先在本项目业务目录改动(core/, ui/, scene/, project/, ssbo_component/, TransformGizmo/),避免直接修改第三方目录。
|
||||
|
||||
## 5. 完整 Python 文件索引(含行数)
|
||||
|
||||
| 文件 | 行数 |
|
||||
|---|---:|
|
||||
| core/__init__.py | 23 |
|
||||
| core/collision_manager.py | 1050 |
|
||||
| core/Command_System.py | 576 |
|
||||
| core/CustomMouseController.py | 185 |
|
||||
| core/event_handler.py | 576 |
|
||||
| core/imgui_style_manager.py | 428 |
|
||||
| core/imgui_webview.py | 189 |
|
||||
| core/InfoPanelManager.py | 1460 |
|
||||
| core/model_drag_drop.py | 485 |
|
||||
| core/render_pipeline_utils.py | 17 |
|
||||
| core/resource_manager.py | 471 |
|
||||
| core/script_system.py | 817 |
|
||||
| core/selection.py | 2461 |
|
||||
| core/selection_outline.py | 263 |
|
||||
| core/terrain_manager.py | 571 |
|
||||
| core/tool_manager.py | 133 |
|
||||
| core/vr/__init__.py | 31 |
|
||||
| core/vr/config/__init__.py | 8 |
|
||||
| core/vr/config/joystick_config.py | 226 |
|
||||
| core/vr/config/shadow_stage.py | 145 |
|
||||
| core/vr/config/vr_config.py | 216 |
|
||||
| core/vr/interaction/__init__.py | 9 |
|
||||
| core/vr/interaction/actions.py | 484 |
|
||||
| core/vr/interaction/grab.py | 329 |
|
||||
| core/vr/interaction/joystick.py | 561 |
|
||||
| core/vr/interaction/teleport.py | 331 |
|
||||
| core/vr/performance/__init__.py | 9 |
|
||||
| core/vr/performance/monitoring.py | 972 |
|
||||
| core/vr/performance/optimization.py | 287 |
|
||||
| core/vr/rendering/__init__.py | 10 |
|
||||
| core/vr/rendering/stages.py | 712 |
|
||||
| core/vr/testing/__init__.py | 6 |
|
||||
| core/vr/testing/test_mode.py | 609 |
|
||||
| core/vr/tracking/__init__.py | 9 |
|
||||
| core/vr/tracking/controllers.py | 391 |
|
||||
| core/vr/visualization/__init__.py | 8 |
|
||||
| core/vr/visualization/controllers.py | 631 |
|
||||
| core/vr/visualization/effects.py | 174 |
|
||||
| core/vr_manager.py | 2970 |
|
||||
| core/world.py | 1059 |
|
||||
| gui/__init__.py | 7 |
|
||||
| main.py | 770 |
|
||||
| project/__init__.py | 10 |
|
||||
| project/project_manager.py | 749 |
|
||||
| scene/__init__.py | 10 |
|
||||
| scene/scene_manager.py | 17 |
|
||||
| scene/scene_manager_convert_tiles_mixin.py | 473 |
|
||||
| scene/scene_manager_impl.py | 15 |
|
||||
| scene/scene_manager_io_mixin.py | 962 |
|
||||
| scene/scene_manager_light_mixin.py | 403 |
|
||||
| scene/scene_manager_model_mixin.py | 919 |
|
||||
| scene/scene_manager_serialization_mixin.py | 749 |
|
||||
| scene/tree_roles.py | 3 |
|
||||
| scene/util.py | 241 |
|
||||
| scripts/a.py | 25 |
|
||||
| scripts/BouncerScript.py | 99 |
|
||||
| scripts/ColorChangerScript.py | 157 |
|
||||
| scripts/ComboAnimatorScript.py | 36 |
|
||||
| scripts/example_script.py | 44 |
|
||||
| scripts/FollowerScript.py | 66 |
|
||||
| scripts/MoverScript.py | 88 |
|
||||
| scripts/R_P.py | 110 |
|
||||
| scripts/R_R.py | 110 |
|
||||
| scripts/Rotate_H_Script.py | 181 |
|
||||
| scripts/Rotate_P_Script.py | 181 |
|
||||
| scripts/Rotate_R_Script.py | 44 |
|
||||
| scripts/RotatorScript.py | 36 |
|
||||
| scripts/ScalerScript.py | 88 |
|
||||
| scripts/test_quick_script.py | 25 |
|
||||
| scripts/TestMover.py | 38 |
|
||||
| scripts/TestRotator.py | 25 |
|
||||
| scripts/TestScaler.py | 25 |
|
||||
| ssbo_component/demo_component.py | 70 |
|
||||
| ssbo_component/ssbo_controller.py | 1055 |
|
||||
| ssbo_component/ssbo_editor.py | 782 |
|
||||
| Start_Run.py | 42 |
|
||||
| templates/main_template.py | 1282 |
|
||||
| tools/open_source_rate.py | 73 |
|
||||
| TransformGizmo/events.py | 14 |
|
||||
| TransformGizmo/move_gizmo.py | 950 |
|
||||
| TransformGizmo/rotate_gizmo.py | 1412 |
|
||||
| TransformGizmo/scale_gizmo.py | 860 |
|
||||
| TransformGizmo/transform_gizmo.py | 469 |
|
||||
| ui/Builtin/__init__.py | 0 |
|
||||
| ui/Builtin/Elements.py | 242 |
|
||||
| ui/Builtin/LUIBlockText.py | 74 |
|
||||
| ui/Builtin/LUIButton.py | 169 |
|
||||
| ui/Builtin/LUICanvas.py | 97 |
|
||||
| ui/Builtin/LUICheckbox.py | 65 |
|
||||
| ui/Builtin/LUIFormattedLabel.py | 38 |
|
||||
| ui/Builtin/LUIFrame.py | 54 |
|
||||
| ui/Builtin/LUIHorizontalLayout.py | 13 |
|
||||
| ui/Builtin/LUIInitialState.py | 32 |
|
||||
| ui/Builtin/LUIInputField.py | 180 |
|
||||
| ui/Builtin/LUIInputHandler.py | 5 |
|
||||
| ui/Builtin/LUILabel.py | 63 |
|
||||
| ui/Builtin/LUILayouts.py | 80 |
|
||||
| ui/Builtin/LUIObject.py | 12 |
|
||||
| ui/Builtin/LUIProgressbar.py | 54 |
|
||||
| ui/Builtin/LUIRadiobox.py | 73 |
|
||||
| ui/Builtin/LUIRadioboxGroup.py | 31 |
|
||||
| ui/Builtin/LUIRegion.py | 5 |
|
||||
| ui/Builtin/LUIRoot.py | 5 |
|
||||
| ui/Builtin/LUIScrollableRegion.py | 119 |
|
||||
| ui/Builtin/LUISelectbox.py | 158 |
|
||||
| ui/Builtin/LUISkin.py | 34 |
|
||||
| ui/Builtin/LUISlider.py | 185 |
|
||||
| ui/Builtin/LUISprite.py | 12 |
|
||||
| ui/Builtin/LUISpriteButton.py | 23 |
|
||||
| ui/Builtin/LUITabbedFrame.py | 77 |
|
||||
| ui/Builtin/LUIVerticalLayout.py | 13 |
|
||||
| ui/Builtin/RectTransform.py | 73 |
|
||||
| ui/icon_manager.py | 123 |
|
||||
| ui/LUI/__init__.py | 1 |
|
||||
| ui/LUI/lui_function_components.py | 1149 |
|
||||
| ui/LUI/lui_function_properties.py | 1601 |
|
||||
| ui/LUI/lui_manager_editor.py | 1593 |
|
||||
| ui/LUI/lui_manager_interaction.py | 1351 |
|
||||
| ui/LUI/lui_shared.py | 62 |
|
||||
| ui/lui_function.py | 31 |
|
||||
| ui/lui_manager.py | 199 |
|
||||
| ui/panels/__init__.py | 0 |
|
||||
| ui/panels/animation_tools.py | 1390 |
|
||||
| ui/panels/app_actions.py | 1097 |
|
||||
| ui/panels/create_actions.py | 147 |
|
||||
| ui/panels/dialog_panels.py | 992 |
|
||||
| ui/panels/editor_panels.py | 20 |
|
||||
| ui/panels/editor_panels_center.py | 187 |
|
||||
| ui/panels/editor_panels_left.py | 543 |
|
||||
| ui/panels/editor_panels_right.py | 742 |
|
||||
| ui/panels/editor_panels_right_collision.py | 210 |
|
||||
| ui/panels/editor_panels_right_material.py | 222 |
|
||||
| ui/panels/editor_panels_right_transform.py | 89 |
|
||||
| ui/panels/editor_panels_top.py | 319 |
|
||||
| ui/panels/interaction_panels.py | 128 |
|
||||
| ui/panels/object_factory.py | 390 |
|
||||
| ui/panels/panel_delegates.py | 512 |
|
||||
| ui/panels/property_helpers.py | 1583 |
|
||||
| ui/panels/runtime_actions.py | 356 |
|
||||
| ui/panels/script_panels.py | 287 |
|
||||
| ui/Skins/__init__.py | 0 |
|
||||
| ui/Skins/Default/__init__.py | 0 |
|
||||
| ui/Skins/Metro/__init__.py | 0 |
|
||||
| ui/Skins/Metro/copy_frames.py | 31 |
|
||||
| ui/Skins/Metro/LUIMetroSkin.py | 24 |
|
||||
| ui/widgets.py | 38 |
|
||||
|
||||
## 6. 使用方式(优化流程建议)
|
||||
|
||||
1. 先在 功能模块 -> 关键文件映射 中定位模块。
|
||||
2. 再到 完整 Python 文件索引 按文件名快速跳转。
|
||||
3. 优先处理高行数核心文件(如 core/selection.py, core/vr_manager.py, ui/panels/property_helpers.py)。
|
||||
@ -1,270 +0,0 @@
|
||||
# PROJECT_OPTIMIZATION_ANALYSIS
|
||||
|
||||
> 基于本地代码静态扫描生成(不含远程仓库信息,不含 `RenderPipelineFile/` 第三方目录)。
|
||||
|
||||
- 分析时间: 2026-02-28
|
||||
- 扫描范围: `main.py`, `Start_Run.py`, `core/`, `scene/`, `project/`, `ui/`, `ssbo_component/`, `TransformGizmo/`, `scripts/`, `tools/`, `templates/`
|
||||
|
||||
## 0. 执行进展(非 VR)
|
||||
|
||||
- 已完成 `EditorContext` 适配层:`core/editor_context.py`
|
||||
- 已接入文件:
|
||||
- `core/event_handler.py`
|
||||
- `core/selection.py`
|
||||
- `core/InfoPanelManager.py`
|
||||
- `ui/panels/runtime_actions.py`
|
||||
- `core/terrain_manager.py`
|
||||
- `scene/scene_manager_convert_tiles_mixin.py`
|
||||
- `scene/scene_manager_serialization_mixin.py`
|
||||
- `scene/scene_manager_model_mixin.py`
|
||||
- 效果(本地静态检索):
|
||||
- 直接访问 `world.interface_manager`: `0`
|
||||
- 直接访问 `interface_manager.treeWidget`: `0`
|
||||
- `app.gui_manager` 仅剩注释引用(`ui/panels/editor_panels_left.py`)
|
||||
- Task B 第一轮已落地(`scene_manager_io_mixin.loadScene`):
|
||||
- 已抽出流程 helper:
|
||||
- `_preflight_load_scene`
|
||||
- `_cleanup_after_failed_load`
|
||||
- `_clear_current_scene_for_load`
|
||||
- `_load_scene_root_from_file`
|
||||
- `_bootstrap_scene_tree_for_loaded_root`
|
||||
- `_load_scene_gui_metadata`
|
||||
- `_retry_load_scene`
|
||||
- `loadScene` 行数:`556 -> 366`
|
||||
- 已清理重复异常分支:`_rebuildParentChildRelationships` 内重复 `except` 已移除
|
||||
|
||||
## 1. 总体画像
|
||||
|
||||
- Python 文件: `146`
|
||||
- 代码总行数: `58,371`
|
||||
- `except Exception` / `except:` 总计: `950`
|
||||
- 裸 `except:` 总计: `63`
|
||||
- 旧上下文关键词引用总量:
|
||||
- `interface_manager`: `35`
|
||||
- `treeWidget`: `10`
|
||||
- `gui_manager`: `77`
|
||||
|
||||
结论:
|
||||
|
||||
- Qt 依赖已清理后,当前主要技术债集中在三类:
|
||||
- 过大函数(可维护性差)
|
||||
- 异常处理过宽(问题可观测性差)
|
||||
- 旧 GUI 上下文命名耦合(边界不清晰)
|
||||
|
||||
## 2. 热点文件(按规模/风险)
|
||||
|
||||
### 2.1 超大文件 Top
|
||||
|
||||
1. `core/vr_manager.py` (`3553` 行)
|
||||
2. `core/selection.py` (`2942` 行)
|
||||
3. `core/InfoPanelManager.py` (`1726` 行)
|
||||
4. `ui/LUI/lui_manager_editor.py` (`1724` 行)
|
||||
5. `ui/panels/property_helpers.py` (`1711` 行)
|
||||
6. `ui/LUI/lui_function_properties.py` (`1707` 行)
|
||||
7. `TransformGizmo/rotate_gizmo.py` (`1587` 行)
|
||||
8. `ui/panels/animation_tools.py` (`1579` 行)
|
||||
|
||||
### 2.2 长函数 Top(优先拆分)
|
||||
|
||||
1. `ui/LUI/lui_function_properties.py::_draw_component_properties` (`1441` 行)
|
||||
2. `scene/scene_manager_io_mixin.py::loadScene` (`556` 行)
|
||||
3. `ui/panels/animation_tools.py::_getActor` (`510` 行)
|
||||
4. `main.py::__init__` (`375` 行)
|
||||
5. `ui/LUI/lui_manager_interaction.py::_update_drag` (`348` 行)
|
||||
6. `ui/panels/editor_panels_left.py::_draw_resource_manager` (`310` 行)
|
||||
7. `scene/scene_manager_io_mixin.py::processNode` (`281` 行)
|
||||
8. `core/selection.py::updateGizmoDrag` (`278` 行)
|
||||
|
||||
### 2.3 异常处理密度高(可观测性风险)
|
||||
|
||||
1. `ui/panels/animation_tools.py` (`except* = 85`)
|
||||
2. `core/vr_manager.py` (`71`)
|
||||
3. `core/selection.py` (`57`)
|
||||
4. `ui/panels/property_helpers.py` (`54`)
|
||||
5. `ui/panels/app_actions.py` (`46`)
|
||||
6. `scene/scene_manager_model_mixin.py` (`36`)
|
||||
7. `scene/scene_manager_serialization_mixin.py` (`27`)
|
||||
8. `scene/scene_manager_io_mixin.py` (`20`)
|
||||
9. `project/project_manager.py` (`20`)
|
||||
10. `ui/panels/runtime_actions.py` (`20`)
|
||||
|
||||
裸 `except:` 集中区:
|
||||
|
||||
- `core/selection.py` (`7`)
|
||||
- `scene/scene_manager_model_mixin.py` (`7`)
|
||||
- `ui/panels/editor_panels_right_material.py` (`6`)
|
||||
- `ui/panels/editor_panels_left.py` (`5`)
|
||||
|
||||
### 2.4 旧上下文耦合集中区(历史基线,Task A 前)
|
||||
|
||||
1. `ui/panels/runtime_actions.py` (`gui_manager=29`)
|
||||
2. `core/event_handler.py` (`interface_manager=11`, `gui_manager=11`)
|
||||
3. `ui/panels/editor_panels_right.py` (`gui_manager=18`)
|
||||
4. `scene/scene_manager_serialization_mixin.py` (`interface_manager=6`, `treeWidget=2`, `gui_manager=5`)
|
||||
5. `core/selection.py` (`interface_manager=4`, `treeWidget=1`)
|
||||
6. `core/InfoPanelManager.py` (`interface_manager=4`, `treeWidget=1`)
|
||||
7. `core/terrain_manager.py` (`interface_manager=3`, `treeWidget=2`)
|
||||
|
||||
## 3. 优化优先级(建议执行顺序)
|
||||
|
||||
## P0: 上下文收敛(先做)
|
||||
|
||||
目标: 统一 GUI/场景树访问边界,减少跨模块 `hasattr(..., 'interface_manager')` 与 `treeWidget` 语义残留。
|
||||
|
||||
建议动作:
|
||||
|
||||
1. 引入 `EditorContext`(或 `UIContext`)统一提供:
|
||||
- `get_tree_adapter()`
|
||||
- `get_gui_service()`
|
||||
- `get_selection_service()`
|
||||
2. 在以下文件先改为调用上下文接口:
|
||||
- `core/event_handler.py`
|
||||
- `core/selection.py`
|
||||
- `core/InfoPanelManager.py`
|
||||
- `core/terrain_manager.py`
|
||||
- `scene/scene_manager_serialization_mixin.py`
|
||||
- `scene/scene_manager_convert_tiles_mixin.py`
|
||||
- `ui/panels/runtime_actions.py`
|
||||
|
||||
预期收益:
|
||||
|
||||
- 降低命名残留与多处 `hasattr` 防御代码。
|
||||
- 后续模块拆分时边界更稳定。
|
||||
|
||||
## P1: 大函数拆分(第二阶段)
|
||||
|
||||
目标: 将核心长函数拆分成“流程编排 + 子步骤函数”,减少单函数认知负担。
|
||||
|
||||
建议拆分顺序:
|
||||
|
||||
1. `scene/scene_manager_io_mixin.py::loadScene`
|
||||
2. `main.py::__init__`
|
||||
3. `ui/panels/animation_tools.py::_getActor`
|
||||
4. `core/selection.py::updateGizmoDrag`
|
||||
5. `ui/LUI/lui_function_properties.py::_draw_component_properties`(可按“变换/布局/视觉/交互/脚本”分区)
|
||||
|
||||
预期收益:
|
||||
|
||||
- 回归问题定位更快。
|
||||
- 面板和场景加载逻辑更易测试。
|
||||
|
||||
## P2: 异常处理治理(并行推进)
|
||||
|
||||
目标: 将“吞异常”改为“有边界的降级 + 可追踪日志”。
|
||||
|
||||
建议规则:
|
||||
|
||||
1. 禁止新增裸 `except:`。
|
||||
2. 高风险路径必须记录上下文:
|
||||
- 节点名/资源路径/操作类型/当前工具状态
|
||||
3. 对可恢复错误使用 `warning`,不可恢复错误返回显式失败值。
|
||||
|
||||
优先文件:
|
||||
|
||||
- `ui/panels/animation_tools.py`
|
||||
- `core/vr_manager.py`
|
||||
- `core/selection.py`
|
||||
- `ui/panels/property_helpers.py`
|
||||
- `scene/scene_manager_io_mixin.py`
|
||||
|
||||
## 4. 下一步可直接执行的任务包
|
||||
|
||||
### Task A(推荐先做,1-2 天)
|
||||
|
||||
- 建立 `core/editor_context.py`(或同级命名)
|
||||
- 给 `event_handler/selection/InfoPanelManager/runtime_actions` 接入上下文
|
||||
- 保持外部 API 不变,仅替换内部访问路径
|
||||
|
||||
### Task B(1 天)
|
||||
|
||||
- 重构 `scene_manager_io_mixin.loadScene`:
|
||||
- `preflight`
|
||||
- `clear_old_scene`
|
||||
- `load_bam`
|
||||
- `rebuild_scene_tree`
|
||||
- `post_load_sync`
|
||||
|
||||
### Task C(1 天)
|
||||
|
||||
- 统一异常日志工具(轻量封装)
|
||||
- 首批替换 `animation_tools.py` 与 `property_helpers.py`
|
||||
|
||||
## 4.1 本轮深入分析(非 VR,P1 准备)
|
||||
|
||||
### A) `scene/scene_manager_io_mixin.py::loadScene`(核心优先)
|
||||
|
||||
- 函数规模: `556` 行
|
||||
- 近似圈复杂度: `114`
|
||||
- 关键问题:
|
||||
- 单函数同时承担 8 类职责(校验/清理/加载/树同步/节点递归处理/脚本恢复/材质恢复/重试)。
|
||||
- 内嵌 `processNode` 递归函数长达 `281` 行,可测试性差。
|
||||
- 调试输出密度高(`print` 与注释 `print` 很多),影响可读性和噪声控制。
|
||||
- `scene/scene_manager_io_mixin.py:1026` 与 `scene/scene_manager_io_mixin.py:1032` 存在重复 `except Exception` 分支(可合并)。
|
||||
- `scene/scene_manager_io_mixin.py:946` 的 GUI 重建入口目前仍注释,行为边界不清晰。
|
||||
- 建议拆分(保持外部 API `loadScene` 不变):
|
||||
- `_preflight_scene_file(filename) -> (ok, normalized_path, reason)`
|
||||
- `_cleanup_before_load(tree_widget, retry_count)`
|
||||
- `_load_bam_scene(filename) -> scene_or_none`
|
||||
- `_bootstrap_tree_items(scene, tree_widget)`
|
||||
- `_walk_loaded_scene(scene, tree_widget) -> loaded_nodes`
|
||||
- `_restore_loaded_nodes_state(node_path, processed_lights, loaded_nodes)`(从 `processNode` 中抽)
|
||||
- `_post_load_finalize(scene, loaded_nodes, filename)`
|
||||
- `_retry_load_scene(filename, retry_count, error) -> bool`
|
||||
- 验收标准:
|
||||
- `loadScene` 主体压缩到 `120` 行以内,只保留流程编排。
|
||||
- 节点恢复行为(位置/材质/脚本/可见性)与当前一致。
|
||||
- 失败重试逻辑保持语义一致。
|
||||
- 当前状态(2026-02-28):
|
||||
- 第一轮已完成(预检/清理/加载/树初始化/GUI元数据/重试)。
|
||||
- 剩余主要体积来自内嵌 `processNode`,下一轮应继续提取为独立方法。
|
||||
|
||||
### B) `main.py::__init__`(第二优先)
|
||||
|
||||
- 函数规模: `375` 行
|
||||
- 近似圈复杂度: `15`
|
||||
- 问题性质:
|
||||
- 复杂度不高,但“启动装配职责”过于集中,初始化顺序风险高。
|
||||
- 建议拆分:
|
||||
- `_init_legacy_compat_fields()`
|
||||
- `_init_core_services_non_vr()`(不含 VR)
|
||||
- `_init_imgui_runtime()`
|
||||
- `_init_panel_modules()`
|
||||
- `_init_runtime_state_flags()`
|
||||
- `_bind_input_shortcuts()`
|
||||
- `_init_drag_drop_and_messages()`
|
||||
- 约束:
|
||||
- VR 初始化保持原样,不纳入本轮改造范围。
|
||||
|
||||
### C) `ui/panels/animation_tools.py::_getActor`(第三优先)
|
||||
|
||||
- 函数规模: `510` 行
|
||||
- 近似圈复杂度: `143`
|
||||
- 关键问题:
|
||||
- 路径推断、缓存策略、Actor 构建、autoBind 回退、GLTF 特化混在一个函数中。
|
||||
- 局部嵌套函数层级深,行为分支很难覆盖测试。
|
||||
- 建议拆分:
|
||||
- `_resolve_actor_owner_and_paths(origin_model)`
|
||||
- `_load_actor_from_candidate_paths(owner_model, paths)`
|
||||
- `_load_actor_via_memory_fallback(owner_model, origin_model)`
|
||||
- `_load_actor_via_gltf_special(path)`
|
||||
- `_validate_actor_playable(actor_or_proxy)`
|
||||
- `_cache_actor(owner_model, actor)`
|
||||
- 验收标准:
|
||||
- `_getActor` 保留为流程入口,长度控制到 `150` 行以内。
|
||||
- 保持当前“优先路径加载,失败回退内存/autoBind”的策略不变。
|
||||
|
||||
## 4.2 Task B 执行顺序建议(非 VR)
|
||||
|
||||
1. 先拆 `loadScene`(收益最大,且与 VR 无关)。
|
||||
2. 再整理 `main.__init__`(降低后续模块接入冲突)。
|
||||
3. 最后处理 `_getActor`(风险最高,建议独立提交并做手工回归)。
|
||||
|
||||
## 5. 与现有文档关系
|
||||
|
||||
- 模块总索引: `PROJECT_MODULE_INDEX.md`
|
||||
- Qt 迁移状态: `QT_TO_IMGUI_MIGRATION_CHECKLIST.md`
|
||||
- 历史分析: `IMGUI_MODULE_ANALYSIS.md`
|
||||
|
||||
---
|
||||
|
||||
如果按此路线继续,建议下一轮直接从 **Task A** 开始,我可以先落地上下文适配层并改 4 个高耦合文件。
|
||||
@ -1,62 +0,0 @@
|
||||
# Qt -> ImGui 迁移清单(Qt 清理后状态)
|
||||
|
||||
更新时间:2026-02-28
|
||||
适用范围:`d:\IMGUI\EG` 本地工作区
|
||||
|
||||
## 目标
|
||||
|
||||
- 编辑器主路径稳定运行在 ImGui + Panda3D。
|
||||
- 项目主代码不再依赖 PyQt/PySide 运行。
|
||||
- 后续把历史命名与耦合(`interface_manager/treeWidget/gui_manager`)继续收敛。
|
||||
|
||||
## 当前状态总览
|
||||
|
||||
- `PyQt/PySide/Qt` 在项目代码主路径(排除 `RenderPipelineFile`)扫描结果:`0`。
|
||||
- 依赖清单已从 Qt 运行依赖切到 ImGui 运行依赖。
|
||||
- `ui/widgets.py` 已改为 legacy 占位模块,避免误用 Qt 路径。
|
||||
|
||||
## 已完成(Done)
|
||||
|
||||
- [x] `core/InfoPanelManager.py`:去除 Qt 直接导入。
|
||||
- [x] `core/selection.py`:光标逻辑改为 Panda3D 路径。
|
||||
- [x] `scene/scene_manager_convert_tiles_mixin.py`:去掉 `QProgressDialog`,改为非 Qt 进度反馈。
|
||||
- [x] `ui/widgets.py`:替换为 ImGui 迁移提示占位模块。
|
||||
- [x] `ui/icon_manager.py`:替换为无 Qt 的兼容层实现。
|
||||
- [x] `core/world.py` / `core/vr_manager.py` / `core/vr/testing/test_mode.py`:去除 `qtWidget` 语义依赖。
|
||||
- [x] `requirements/*`:清理 Qt/PySide 依赖项并同步部署文档。
|
||||
- [x] `main.py`:补齐兼容字段初始化(`gui_elements/gui_manager/interface_manager/guiEditMode/currentGUITool`)。
|
||||
- [x] `scene/scene_manager_io_mixin.py`:修复 `_find_scrip_in_directory` 拼写错误。
|
||||
- [x] `main.py`:入口改为 `if __name__ == "__main__":`,消除导入即运行副作用。
|
||||
- [x] `core/event_handler.py`:新增场景树访问 helper,替换 `interface_manager.treeWidget` 直连。
|
||||
- [x] `core/selection.py`:统一树控件清空逻辑(`_get_tree_widget/_clear_tree_selection`)。
|
||||
- [x] `ui/panels/runtime_actions.py`:统一 `gui_manager` 访问与 `gui_elements` 追加入口。
|
||||
- [x] `core/InfoPanelManager.py`:统一场景树控件访问 helper。
|
||||
- [x] `ui/panels/editor_panels.py`:按布局拆分为 4 个 mixin(`top/left/right/center`),保留门面类组合。
|
||||
|
||||
## 待完成(Next)
|
||||
|
||||
### N1 场景树接口收敛(高优先级)
|
||||
|
||||
- 目标:移除 `treeWidget` 语义,统一走 ImGui 场景树接口。
|
||||
- 影响路径:`core/event_handler.py`、`scene/scene_manager_*_mixin.py`、`core/terrain_manager.py` 等。
|
||||
|
||||
### N2 GUI 管理上下文收敛(高优先级)
|
||||
|
||||
- 目标:将 `interface_manager/gui_manager/gui_elements` 统一到单一上下文对象。
|
||||
- 影响路径:`project/project_manager.py`、`scene/scene_manager_io_mixin.py`、`ui/panels/*`。
|
||||
|
||||
### N3 入口规范化与回归脚本(中优先级)
|
||||
|
||||
- 目标:减少脚本式副作用,建立最小回归检查。
|
||||
- 建议覆盖:启动、导入模型、选择变换、保存加载、脚本挂载。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- 不安装 PyQt/PySide 时,`python Start_Run.py` 能进入编辑器主界面。
|
||||
- 场景导入/保存/加载流程不触发 Qt 相关异常。
|
||||
- 关键交互(选择、变换、场景树)行为与清理前一致。
|
||||
|
||||
## 关联文档
|
||||
|
||||
- 模块分析: [IMGUI_MODULE_ANALYSIS.md](d:/IMGUI/EG/IMGUI_MODULE_ANALYSIS.md)
|
||||
- 全量目录与脚本清单: [PROJECT_FULL_CATALOG.md](d:/IMGUI/EG/PROJECT_FULL_CATALOG.md)
|
||||
@ -1 +0,0 @@
|
||||
[]
|
||||
Binary file not shown.
@ -1,102 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
跳跃脚本 - 让对象产生上下跳跃效果
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
import math
|
||||
|
||||
class BouncerScript(ScriptBase):
|
||||
"""跳跃脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 跳跃参数
|
||||
self.jump_height = 2.0 # 跳跃高度
|
||||
self.jump_speed = 3.0 # 跳跃速度 (跳跃/秒)
|
||||
self.bounce_type = "sine" # 跳跃类型: "sine", "abs_sine", "square"
|
||||
|
||||
# 内部变量
|
||||
self.time_accumulator = 0.0 # 时间累积器
|
||||
self.original_y = None # 原始Y位置
|
||||
self.is_bouncing = True # 是否正在跳跃
|
||||
self.bounce_direction = 1 # 跳跃方向
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("跳跃脚本启动!")
|
||||
self.log(f"跳跃参数: 高度={self.jump_height}, 速度={self.jump_speed}, 类型={self.bounce_type}")
|
||||
|
||||
# 记录原始Y位置
|
||||
self.original_y = self.gameObject.getZ() # Z轴是高度
|
||||
self.log(f"原始高度: {self.original_y}")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
if not self.is_bouncing:
|
||||
return
|
||||
|
||||
# 累积时间
|
||||
self.time_accumulator += dt * self.bounce_direction
|
||||
|
||||
# 根据类型计算跳跃高度
|
||||
if self.bounce_type == "sine":
|
||||
# 标准正弦波跳跃
|
||||
height_offset = math.sin(self.time_accumulator * self.jump_speed * 2 * math.pi) * self.jump_height
|
||||
elif self.bounce_type == "abs_sine":
|
||||
# 绝对值正弦波(始终向上)
|
||||
height_offset = abs(math.sin(self.time_accumulator * self.jump_speed * 2 * math.pi)) * self.jump_height
|
||||
elif self.bounce_type == "square":
|
||||
# 方波跳跃(突然跳起落下)
|
||||
sine_val = math.sin(self.time_accumulator * self.jump_speed * 2 * math.pi)
|
||||
height_offset = self.jump_height if sine_val > 0 else 0
|
||||
else:
|
||||
height_offset = 0
|
||||
|
||||
# 应用跳跃
|
||||
current_pos = self.gameObject.getPos()
|
||||
new_z = self.original_y + height_offset
|
||||
self.gameObject.setPos(current_pos.getX(), current_pos.getY(), new_z)
|
||||
|
||||
def set_bounce_parameters(self, height=None, speed=None, bounce_type=None):
|
||||
"""设置跳跃参数"""
|
||||
if height is not None:
|
||||
self.jump_height = height
|
||||
if speed is not None:
|
||||
self.jump_speed = speed
|
||||
if bounce_type is not None and bounce_type in ["sine", "abs_sine", "square"]:
|
||||
self.bounce_type = bounce_type
|
||||
|
||||
self.log(f"跳跃参数更新: 高度={self.jump_height}, 速度={self.jump_speed}, 类型={self.bounce_type}")
|
||||
|
||||
def toggle_bouncing(self):
|
||||
"""切换跳跃状态"""
|
||||
self.is_bouncing = not self.is_bouncing
|
||||
status = "恢复" if self.is_bouncing else "暂停"
|
||||
self.log(f"跳跃{status}")
|
||||
|
||||
def reverse_direction(self):
|
||||
"""反转跳跃方向"""
|
||||
self.bounce_direction *= -1
|
||||
direction = "正向" if self.bounce_direction > 0 else "反向"
|
||||
self.log(f"跳跃方向改为{direction}")
|
||||
|
||||
def reset_position(self):
|
||||
"""重置到原始高度"""
|
||||
if self.original_y is not None:
|
||||
current_pos = self.gameObject.getPos()
|
||||
self.gameObject.setPos(current_pos.getX(), current_pos.getY(), self.original_y)
|
||||
self.time_accumulator = 0.0
|
||||
self.log("位置已重置到原始高度")
|
||||
|
||||
def jump_once(self):
|
||||
"""执行一次跳跃"""
|
||||
self.time_accumulator = 0.0
|
||||
self.log("执行单次跳跃")
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("跳跃脚本停止")
|
||||
@ -1,160 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
颜色变化脚本 - 让对象颜色产生循环变化
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
from panda3d.core import Vec4
|
||||
import math
|
||||
|
||||
class ColorChangerScript(ScriptBase):
|
||||
"""颜色变化脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 颜色参数
|
||||
self.color_speed = 1.0 # 颜色变化速度 (周期/秒)
|
||||
self.color_mode = "rainbow" # 颜色模式: "rainbow", "pulse", "fade", "strobe"
|
||||
self.base_color = Vec4(1, 1, 1, 1) # 基础颜色
|
||||
self.intensity = 1.0 # 颜色强度
|
||||
|
||||
# 内部变量
|
||||
self.time_accumulator = 0.0 # 时间累积器
|
||||
self.original_color = None # 原始颜色
|
||||
self.is_changing = True # 是否正在变化
|
||||
self.strobe_state = False # 闪烁状态
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("颜色变化脚本启动!")
|
||||
self.log(f"颜色参数: 速度={self.color_speed}, 模式={self.color_mode}, 强度={self.intensity}")
|
||||
|
||||
# 记录原始颜色
|
||||
self.original_color = self.gameObject.getColor()
|
||||
self.log(f"原始颜色: {self.original_color}")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
if not self.is_changing:
|
||||
return
|
||||
|
||||
# 累积时间
|
||||
self.time_accumulator += dt
|
||||
|
||||
# 根据模式计算新颜色
|
||||
if self.color_mode == "rainbow":
|
||||
new_color = self._calculate_rainbow_color()
|
||||
elif self.color_mode == "pulse":
|
||||
new_color = self._calculate_pulse_color()
|
||||
elif self.color_mode == "fade":
|
||||
new_color = self._calculate_fade_color()
|
||||
elif self.color_mode == "strobe":
|
||||
new_color = self._calculate_strobe_color()
|
||||
else:
|
||||
new_color = self.base_color
|
||||
|
||||
# 应用颜色
|
||||
self.gameObject.setColor(new_color)
|
||||
|
||||
def _calculate_rainbow_color(self):
|
||||
"""计算彩虹颜色"""
|
||||
# 使用HSV到RGB的转换创建彩虹效果
|
||||
hue = (self.time_accumulator * self.color_speed) % 1.0
|
||||
|
||||
# 简单的HSV到RGB转换
|
||||
i = int(hue * 6.0)
|
||||
f = (hue * 6.0) - i
|
||||
p = 0.0
|
||||
q = 1.0 - f
|
||||
t = f
|
||||
|
||||
if i % 6 == 0:
|
||||
r, g, b = 1.0, t, p
|
||||
elif i % 6 == 1:
|
||||
r, g, b = q, 1.0, p
|
||||
elif i % 6 == 2:
|
||||
r, g, b = p, 1.0, t
|
||||
elif i % 6 == 3:
|
||||
r, g, b = p, q, 1.0
|
||||
elif i % 6 == 4:
|
||||
r, g, b = t, p, 1.0
|
||||
else:
|
||||
r, g, b = 1.0, p, q
|
||||
|
||||
return Vec4(r * self.intensity, g * self.intensity, b * self.intensity, 1.0)
|
||||
|
||||
def _calculate_pulse_color(self):
|
||||
"""计算脉冲颜色"""
|
||||
pulse = (math.sin(self.time_accumulator * self.color_speed * 2 * math.pi) + 1.0) / 2.0
|
||||
multiplier = pulse * self.intensity
|
||||
return Vec4(
|
||||
self.base_color.getX() * multiplier,
|
||||
self.base_color.getY() * multiplier,
|
||||
self.base_color.getZ() * multiplier,
|
||||
self.base_color.getW()
|
||||
)
|
||||
|
||||
def _calculate_fade_color(self):
|
||||
"""计算淡入淡出颜色"""
|
||||
fade = (math.sin(self.time_accumulator * self.color_speed * 2 * math.pi) + 1.0) / 2.0
|
||||
alpha = fade * self.intensity
|
||||
return Vec4(
|
||||
self.base_color.getX(),
|
||||
self.base_color.getY(),
|
||||
self.base_color.getZ(),
|
||||
alpha
|
||||
)
|
||||
|
||||
def _calculate_strobe_color(self):
|
||||
"""计算闪烁颜色"""
|
||||
# 根据时间间隔切换状态
|
||||
interval = 1.0 / (self.color_speed * 2) # 闪烁间隔
|
||||
if int(self.time_accumulator / interval) % 2 == 0:
|
||||
return Vec4(
|
||||
self.base_color.getX() * self.intensity,
|
||||
self.base_color.getY() * self.intensity,
|
||||
self.base_color.getZ() * self.intensity,
|
||||
self.base_color.getW()
|
||||
)
|
||||
else:
|
||||
return Vec4(0.1, 0.1, 0.1, self.base_color.getW()) # 暗色状态
|
||||
|
||||
def set_color_parameters(self, speed=None, mode=None, base_color=None, intensity=None):
|
||||
"""设置颜色参数"""
|
||||
if speed is not None:
|
||||
self.color_speed = speed
|
||||
if mode is not None and mode in ["rainbow", "pulse", "fade", "strobe"]:
|
||||
self.color_mode = mode
|
||||
if base_color is not None:
|
||||
self.base_color = base_color
|
||||
if intensity is not None:
|
||||
self.intensity = intensity
|
||||
|
||||
self.log(f"颜色参数更新: 速度={self.color_speed}, 模式={self.color_mode}, 强度={self.intensity}")
|
||||
|
||||
def toggle_color_change(self):
|
||||
"""切换颜色变化状态"""
|
||||
self.is_changing = not self.is_changing
|
||||
status = "恢复" if self.is_changing else "暂停"
|
||||
self.log(f"颜色变化{status}")
|
||||
|
||||
def reset_color(self):
|
||||
"""重置到原始颜色"""
|
||||
if self.original_color:
|
||||
self.gameObject.setColor(self.original_color)
|
||||
self.time_accumulator = 0.0
|
||||
self.log("颜色已重置到原始值")
|
||||
|
||||
def set_solid_color(self, r=1.0, g=1.0, b=1.0, a=1.0):
|
||||
"""设置固定颜色"""
|
||||
color = Vec4(r, g, b, a)
|
||||
self.gameObject.setColor(color)
|
||||
self.base_color = color
|
||||
self.log(f"设置固定颜色: {color}")
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("颜色变化脚本停止")
|
||||
@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
复合动画脚本 - 结合旋转和跳跃效果
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
import math
|
||||
|
||||
class ComboAnimatorScript(ScriptBase):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.time = 0.0
|
||||
self.original_pos = None
|
||||
self.is_active = True
|
||||
|
||||
def start(self):
|
||||
self.log("复合动画脚本启动!")
|
||||
self.original_pos = self.gameObject.getPos()
|
||||
|
||||
def update(self, dt):
|
||||
if not self.is_active:
|
||||
return
|
||||
|
||||
self.time += dt
|
||||
|
||||
# 旋转效果
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
new_h = current_hpr.getX() + 45.0 * dt
|
||||
self.gameObject.setHpr(new_h, current_hpr.getY(), current_hpr.getZ())
|
||||
|
||||
# 跳跃效果
|
||||
if self.original_pos:
|
||||
bounce_offset = abs(math.sin(self.time * 3.0)) * 1.0
|
||||
self.gameObject.setZ(self.original_pos.getZ() + bounce_offset)
|
||||
|
||||
def on_destroy(self):
|
||||
self.log("复合动画脚本停止")
|
||||
@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
跟随脚本 - 让对象跟随指定的目标对象
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
from panda3d.core import Vec3
|
||||
|
||||
class FollowerScript(ScriptBase):
|
||||
"""跟随脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.target = None # 跟随目标
|
||||
self.follow_speed = 5.0 # 跟随速度
|
||||
self.follow_distance = 2.0 # 跟随距离
|
||||
self.is_following = True # 是否正在跟随
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("跟随脚本启动!")
|
||||
self.log(f"跟随参数: 速度={self.follow_speed}, 距离={self.follow_distance}")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
if not self.is_following or self.target is None:
|
||||
return
|
||||
|
||||
target_pos = self.target.getPos()
|
||||
current_pos = self.gameObject.getPos()
|
||||
|
||||
# 计算目标方向
|
||||
direction = target_pos - current_pos
|
||||
distance = direction.length()
|
||||
|
||||
# 如果距离大于跟随距离,则移动
|
||||
if distance > self.follow_distance:
|
||||
if distance > 0:
|
||||
direction.normalize()
|
||||
|
||||
# 计算目标位置(保持跟随距离)
|
||||
target_follow_pos = target_pos - direction * self.follow_distance
|
||||
|
||||
# 平滑移动到目标位置
|
||||
move_direction = target_follow_pos - current_pos
|
||||
move_distance = move_direction.length()
|
||||
|
||||
if move_distance > 0:
|
||||
move_direction.normalize()
|
||||
move_amount = min(self.follow_speed * dt, move_distance)
|
||||
new_pos = current_pos + move_direction * move_amount
|
||||
self.gameObject.setPos(new_pos)
|
||||
|
||||
# 朝向目标
|
||||
self.gameObject.lookAt(target_pos)
|
||||
|
||||
def set_target(self, target):
|
||||
"""设置跟随目标"""
|
||||
self.target = target
|
||||
if target:
|
||||
self.log(f"设置跟随目标: {target.getName()}")
|
||||
else:
|
||||
self.log("清除跟随目标")
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("跟随脚本停止")
|
||||
@ -1,91 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
移动脚本 - 让对象在指定方向上来回移动
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
import math
|
||||
|
||||
class MoverScript(ScriptBase):
|
||||
"""移动脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 移动参数
|
||||
self.move_distance = 5.0 # 移动距离
|
||||
self.move_speed = 2.0 # 移动速度 (单位/秒)
|
||||
self.move_axis = "x" # 移动轴: "x", "y", "z"
|
||||
|
||||
# 内部变量
|
||||
self.start_position = None # 起始位置
|
||||
self.current_direction = 1 # 当前移动方向: 1或-1
|
||||
self.current_distance = 0.0 # 当前移动距离
|
||||
self.is_moving = True # 是否正在移动
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("移动脚本启动!")
|
||||
self.log(f"移动参数: 距离={self.move_distance}, 速度={self.move_speed}, 轴={self.move_axis}")
|
||||
|
||||
# 记录起始位置
|
||||
self.start_position = self.gameObject.getPos()
|
||||
self.log(f"起始位置: {self.start_position}")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
if not self.is_moving or self.start_position is None:
|
||||
return
|
||||
|
||||
# 计算移动增量
|
||||
move_delta = self.move_speed * dt * self.current_direction
|
||||
self.current_distance += abs(move_delta)
|
||||
|
||||
# 检查是否需要改变方向
|
||||
if self.current_distance >= self.move_distance:
|
||||
self.current_direction *= -1
|
||||
self.current_distance = 0.0
|
||||
|
||||
# 应用移动
|
||||
current_pos = self.gameObject.getPos()
|
||||
new_pos = [current_pos.getX(), current_pos.getY(), current_pos.getZ()]
|
||||
|
||||
if self.move_axis == "x":
|
||||
new_pos[0] += move_delta
|
||||
elif self.move_axis == "y":
|
||||
new_pos[1] += move_delta
|
||||
elif self.move_axis == "z":
|
||||
new_pos[2] += move_delta
|
||||
|
||||
self.gameObject.setPos(new_pos[0], new_pos[1], new_pos[2])
|
||||
|
||||
def set_move_parameters(self, distance=None, speed=None, axis=None):
|
||||
"""设置移动参数"""
|
||||
if distance is not None:
|
||||
self.move_distance = distance
|
||||
if speed is not None:
|
||||
self.move_speed = speed
|
||||
if axis is not None and axis in ["x", "y", "z"]:
|
||||
self.move_axis = axis
|
||||
|
||||
self.log(f"移动参数更新: 距离={self.move_distance}, 速度={self.move_speed}, 轴={self.move_axis}")
|
||||
|
||||
def toggle_movement(self):
|
||||
"""切换移动状态"""
|
||||
self.is_moving = not self.is_moving
|
||||
status = "恢复" if self.is_moving else "暂停"
|
||||
self.log(f"移动{status}")
|
||||
|
||||
def reset_position(self):
|
||||
"""重置到起始位置"""
|
||||
if self.start_position:
|
||||
self.gameObject.setPos(self.start_position)
|
||||
self.current_distance = 0.0
|
||||
self.current_direction = 1
|
||||
self.log("位置已重置到起始点")
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("移动脚本停止")
|
||||
@ -1,133 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
旋转脚本 - 让对象持续旋转
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
|
||||
class RotatorScript(ScriptBase):
|
||||
"""旋转脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒)
|
||||
self.max_angle = 30.0 # 最大旋转角度(相对于初始角度)
|
||||
self.direction = 1
|
||||
self.current_offset = 0.0 # 当前相对于初始角度的偏移
|
||||
self.initial_angle = None # 模型的初始角度
|
||||
self.is_rotating = True # 是否正在旋转
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("旋转脚本启动!")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"最大旋转角度: ±{self.max_angle}度")
|
||||
|
||||
# 记录模型的初始角度
|
||||
if self.gameObject:
|
||||
initial_hpr = self.gameObject.getHpr()
|
||||
self.initial_angle = initial_hpr.getZ() # 记录Z轴的初始角度
|
||||
self.log(f"模型初始角度: {self.initial_angle}度")
|
||||
self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
else:
|
||||
self.log("⚠️ 无法获取游戏对象,使用默认初始角度0")
|
||||
self.initial_angle = 0.0
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
if not self.is_rotating or self.initial_angle is None:
|
||||
return
|
||||
|
||||
# 计算角度变化量
|
||||
delta_angle = self.rotation_speed_y * dt * self.direction
|
||||
self.current_offset += delta_angle
|
||||
|
||||
# 如果超出角度范围,则反向并限制在边界
|
||||
if self.current_offset > self.max_angle:
|
||||
self.current_offset = self.max_angle
|
||||
self.direction *= -1
|
||||
elif self.current_offset < -self.max_angle:
|
||||
self.current_offset = -self.max_angle
|
||||
self.direction *= -1
|
||||
|
||||
# 计算最终角度(初始角度 + 偏移量)
|
||||
final_angle = self.initial_angle + self.current_offset
|
||||
|
||||
# 设置新的旋转(只改变Z轴,保持其他不变)
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), final_angle, current_hpr.getZ())
|
||||
|
||||
# if not self.is_rotating:
|
||||
# return
|
||||
#
|
||||
# # 获取当前旋转并应用增量
|
||||
# current_hpr = self.gameObject.getHpr()
|
||||
# new_r = current_hpr.getZ() + self.rotation_speed_y * dt
|
||||
# self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("旋转脚本停止")
|
||||
|
||||
# ==================== 控制方法 ====================
|
||||
|
||||
def set_max_angle(self, new_max_angle):
|
||||
"""
|
||||
设置新的最大旋转角度
|
||||
|
||||
Args:
|
||||
new_max_angle: 新的最大角度值
|
||||
"""
|
||||
self.max_angle = new_max_angle
|
||||
self.log(f"最大旋转角度已设置为: ±{self.max_angle}度")
|
||||
if self.initial_angle is not None:
|
||||
self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
|
||||
def set_rotation_speed(self, new_speed):
|
||||
"""
|
||||
设置新的旋转速度
|
||||
|
||||
Args:
|
||||
new_speed: 新的旋转速度(度/秒)
|
||||
"""
|
||||
self.rotation_speed_y = new_speed
|
||||
self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒")
|
||||
|
||||
def pause_rotation(self):
|
||||
"""暂停旋转"""
|
||||
self.is_rotating = False
|
||||
self.log("旋转已暂停")
|
||||
|
||||
def resume_rotation(self):
|
||||
"""恢复旋转"""
|
||||
self.is_rotating = True
|
||||
self.log("旋转已恢复")
|
||||
|
||||
def reset_to_initial_angle(self):
|
||||
"""重置到初始角度"""
|
||||
if self.initial_angle is not None and self.gameObject:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), self.initial_angle)
|
||||
self.current_offset = 0.0
|
||||
self.direction = 1
|
||||
self.log(f"已重置到初始角度: {self.initial_angle}度")
|
||||
|
||||
def get_current_info(self):
|
||||
"""获取当前旋转信息"""
|
||||
if self.gameObject and self.initial_angle is not None:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
current_angle = current_hpr.getZ()
|
||||
self.log("=== 当前旋转信息 ===")
|
||||
self.log(f"初始角度: {self.initial_angle}度")
|
||||
self.log(f"当前角度: {current_angle}度")
|
||||
self.log(f"偏移量: {self.current_offset}度")
|
||||
self.log(f"最大角度: ±{self.max_angle}度")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}")
|
||||
self.log("=== 信息结束 ===")
|
||||
else:
|
||||
self.log("无法获取旋转信息")
|
||||
@ -1,133 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
旋转脚本 - 让对象持续旋转
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
|
||||
class RotatorScript(ScriptBase):
|
||||
"""旋转脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒)
|
||||
self.max_angle = 30.0 # 最大旋转角度(相对于初始角度)
|
||||
self.direction = 1
|
||||
self.current_offset = 0.0 # 当前相对于初始角度的偏移
|
||||
self.initial_angle = None # 模型的初始角度
|
||||
self.is_rotating = True # 是否正在旋转
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("旋转脚本启动!")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"最大旋转角度: ±{self.max_angle}度")
|
||||
|
||||
# 记录模型的初始角度
|
||||
if self.gameObject:
|
||||
initial_hpr = self.gameObject.getHpr()
|
||||
self.initial_angle = initial_hpr.getZ() # 记录Z轴的初始角度
|
||||
self.log(f"模型初始角度: {self.initial_angle}度")
|
||||
self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
else:
|
||||
self.log("⚠️ 无法获取游戏对象,使用默认初始角度0")
|
||||
self.initial_angle = 0.0
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
if not self.is_rotating or self.initial_angle is None:
|
||||
return
|
||||
|
||||
# 计算角度变化量
|
||||
delta_angle = self.rotation_speed_y * dt * self.direction
|
||||
self.current_offset += delta_angle
|
||||
|
||||
# 如果超出角度范围,则反向并限制在边界
|
||||
if self.current_offset > self.max_angle:
|
||||
self.current_offset = self.max_angle
|
||||
self.direction *= -1
|
||||
elif self.current_offset < -self.max_angle:
|
||||
self.current_offset = -self.max_angle
|
||||
self.direction *= -1
|
||||
|
||||
# 计算最终角度(初始角度 + 偏移量)
|
||||
final_angle = self.initial_angle + self.current_offset
|
||||
|
||||
# 设置新的旋转(只改变Z轴,保持其他不变)
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), final_angle)
|
||||
|
||||
# if not self.is_rotating:
|
||||
# return
|
||||
#
|
||||
# # 获取当前旋转并应用增量
|
||||
# current_hpr = self.gameObject.getHpr()
|
||||
# new_r = current_hpr.getZ() + self.rotation_speed_y * dt
|
||||
# self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("旋转脚本停止")
|
||||
|
||||
# ==================== 控制方法 ====================
|
||||
|
||||
def set_max_angle(self, new_max_angle):
|
||||
"""
|
||||
设置新的最大旋转角度
|
||||
|
||||
Args:
|
||||
new_max_angle: 新的最大角度值
|
||||
"""
|
||||
self.max_angle = new_max_angle
|
||||
self.log(f"最大旋转角度已设置为: ±{self.max_angle}度")
|
||||
if self.initial_angle is not None:
|
||||
self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
|
||||
def set_rotation_speed(self, new_speed):
|
||||
"""
|
||||
设置新的旋转速度
|
||||
|
||||
Args:
|
||||
new_speed: 新的旋转速度(度/秒)
|
||||
"""
|
||||
self.rotation_speed_y = new_speed
|
||||
self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒")
|
||||
|
||||
def pause_rotation(self):
|
||||
"""暂停旋转"""
|
||||
self.is_rotating = False
|
||||
self.log("旋转已暂停")
|
||||
|
||||
def resume_rotation(self):
|
||||
"""恢复旋转"""
|
||||
self.is_rotating = True
|
||||
self.log("旋转已恢复")
|
||||
|
||||
def reset_to_initial_angle(self):
|
||||
"""重置到初始角度"""
|
||||
if self.initial_angle is not None and self.gameObject:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), self.initial_angle)
|
||||
self.current_offset = 0.0
|
||||
self.direction = 1
|
||||
self.log(f"已重置到初始角度: {self.initial_angle}度")
|
||||
|
||||
def get_current_info(self):
|
||||
"""获取当前旋转信息"""
|
||||
if self.gameObject and self.initial_angle is not None:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
current_angle = current_hpr.getZ()
|
||||
self.log("=== 当前旋转信息 ===")
|
||||
self.log(f"初始角度: {self.initial_angle}度")
|
||||
self.log(f"当前角度: {current_angle}度")
|
||||
self.log(f"偏移量: {self.current_offset}度")
|
||||
self.log(f"最大角度: ±{self.max_angle}度")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}")
|
||||
self.log("=== 信息结束 ===")
|
||||
else:
|
||||
self.log("无法获取旋转信息")
|
||||
@ -1,215 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
旋转脚本 - 让对象持续旋转
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
|
||||
class RotatorScript(ScriptBase):
|
||||
"""旋转脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒)
|
||||
self.max_angle = 30.0 # 最大旋转角度(相对于初始角度)
|
||||
self.direction = 1
|
||||
self.current_offset = 0.0 # 当前相对于初始角度的偏移
|
||||
self.initial_angle = None # 模型的初始角度
|
||||
self.is_rotating = True # 是否正在旋转
|
||||
|
||||
# 机器人式停顿参数
|
||||
self.pause_duration = 0.5 # 停顿时间(秒)
|
||||
self.current_pause_time = 0.0 # 当前停顿计时
|
||||
self.is_paused = False # 是否正在停顿
|
||||
self.robot_mode = True # 是否启用机器人模式
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("机器人旋转脚本启动!")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"最大旋转角度: ±{self.max_angle}度")
|
||||
self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}")
|
||||
if self.robot_mode:
|
||||
self.log(f"停顿时间: {self.pause_duration}秒")
|
||||
|
||||
# 记录模型的初始角度
|
||||
if self.gameObject:
|
||||
initial_hpr = self.gameObject.getHpr()
|
||||
self.initial_angle = initial_hpr.getY() # 记录Y轴的初始角度(Pitch)
|
||||
self.log(f"模型初始角度: {self.initial_angle}度")
|
||||
self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
else:
|
||||
self.log("⚠️ 无法获取游戏对象,使用默认初始角度0")
|
||||
self.initial_angle = 0.0
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新 - 机器人式旋转"""
|
||||
if not self.is_rotating or self.initial_angle is None:
|
||||
return
|
||||
|
||||
# 如果正在停顿中
|
||||
if self.is_paused:
|
||||
self.current_pause_time += dt
|
||||
if self.current_pause_time >= self.pause_duration:
|
||||
# 停顿结束,继续旋转
|
||||
self.is_paused = False
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"停顿结束,继续旋转,方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
return
|
||||
|
||||
# 计算角度变化量
|
||||
delta_angle = self.rotation_speed_y * dt * self.direction
|
||||
self.current_offset += delta_angle
|
||||
|
||||
# 检查是否到达边界
|
||||
reached_boundary = False
|
||||
if self.current_offset > self.max_angle:
|
||||
self.current_offset = self.max_angle
|
||||
self.direction *= -1
|
||||
reached_boundary = True
|
||||
elif self.current_offset < -self.max_angle:
|
||||
self.current_offset = -self.max_angle
|
||||
self.direction *= -1
|
||||
reached_boundary = True
|
||||
|
||||
# 如果到达边界且启用机器人模式,开始停顿
|
||||
if reached_boundary and self.robot_mode:
|
||||
self.is_paused = True
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"到达边界 ({self.current_offset}°),开始停顿 {self.pause_duration}秒")
|
||||
|
||||
# 计算最终角度(初始角度 + 偏移量)
|
||||
final_angle = self.initial_angle + self.current_offset
|
||||
|
||||
# 设置新的旋转(只改变Y轴,保持其他不变)
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(final_angle, current_hpr.getY(), current_hpr.getZ())
|
||||
|
||||
# if not self.is_rotating:
|
||||
# return
|
||||
#
|
||||
# # 获取当前旋转并应用增量
|
||||
# current_hpr = self.gameObject.getHpr()
|
||||
# new_r = current_hpr.getZ() + self.rotation_speed_y * dt
|
||||
# self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("机器人旋转脚本停止")
|
||||
|
||||
# ==================== 机器人模式控制方法 ====================
|
||||
|
||||
def set_robot_mode(self, enabled=True):
|
||||
"""
|
||||
启用或禁用机器人模式
|
||||
|
||||
Args:
|
||||
enabled: 是否启用机器人模式
|
||||
"""
|
||||
self.robot_mode = enabled
|
||||
self.log(f"机器人模式: {'开启' if enabled else '关闭'}")
|
||||
if not enabled:
|
||||
self.is_paused = False # 如果禁用机器人模式,立即结束停顿
|
||||
|
||||
def set_pause_duration(self, duration):
|
||||
"""
|
||||
设置停顿时间
|
||||
|
||||
Args:
|
||||
duration: 停顿时间(秒)
|
||||
"""
|
||||
self.pause_duration = max(0.1, duration) # 最小0.1秒
|
||||
self.log(f"停顿时间已设置为: {self.pause_duration}秒")
|
||||
|
||||
def set_max_angle(self, new_max_angle):
|
||||
"""
|
||||
设置新的最大旋转角度
|
||||
|
||||
Args:
|
||||
new_max_angle: 新的最大角度值
|
||||
"""
|
||||
self.max_angle = new_max_angle
|
||||
self.log(f"最大旋转角度已设置为: ±{self.max_angle}度")
|
||||
if self.initial_angle is not None:
|
||||
self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
|
||||
def set_rotation_speed(self, new_speed):
|
||||
"""
|
||||
设置新的旋转速度
|
||||
|
||||
Args:
|
||||
new_speed: 新的旋转速度(度/秒)
|
||||
"""
|
||||
self.rotation_speed_y = new_speed
|
||||
self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒")
|
||||
|
||||
def pause_rotation(self):
|
||||
"""暂停旋转"""
|
||||
self.is_rotating = False
|
||||
self.log("旋转已暂停")
|
||||
|
||||
def resume_rotation(self):
|
||||
"""恢复旋转"""
|
||||
self.is_rotating = True
|
||||
self.is_paused = False # 同时结束停顿状态
|
||||
self.log("旋转已恢复")
|
||||
|
||||
def reset_to_initial_angle(self):
|
||||
"""重置到初始角度"""
|
||||
if self.initial_angle is not None and self.gameObject:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), self.initial_angle, current_hpr.getZ())
|
||||
self.current_offset = 0.0
|
||||
self.direction = 1
|
||||
self.is_paused = False
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"已重置到初始角度: {self.initial_angle}度")
|
||||
|
||||
def get_current_info(self):
|
||||
"""获取当前旋转信息"""
|
||||
if self.gameObject and self.initial_angle is not None:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
current_angle = current_hpr.getY()
|
||||
self.log("=== 机器人旋转信息 ===")
|
||||
self.log(f"初始角度: {self.initial_angle}度")
|
||||
self.log(f"当前角度: {current_angle}度")
|
||||
self.log(f"偏移量: {self.current_offset}度")
|
||||
self.log(f"最大角度: ±{self.max_angle}度")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}")
|
||||
self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}")
|
||||
if self.robot_mode:
|
||||
self.log(f"停顿时间: {self.pause_duration}秒")
|
||||
self.log(f"当前状态: {'停顿中' if self.is_paused else '旋转中'}")
|
||||
if self.is_paused:
|
||||
remaining_time = self.pause_duration - self.current_pause_time
|
||||
self.log(f"剩余停顿时间: {remaining_time:.1f}秒")
|
||||
self.log("=== 信息结束 ===")
|
||||
else:
|
||||
self.log("无法获取旋转信息")
|
||||
|
||||
# ==================== 预设配置方法 ====================
|
||||
|
||||
def set_slow_robot_mode(self):
|
||||
"""预设:慢速机器人模式"""
|
||||
self.set_rotation_speed(15.0)
|
||||
self.set_pause_duration(1.0)
|
||||
self.set_robot_mode(True)
|
||||
self.log("已设置为慢速机器人模式")
|
||||
|
||||
def set_fast_robot_mode(self):
|
||||
"""预设:快速机器人模式"""
|
||||
self.set_rotation_speed(45.0)
|
||||
self.set_pause_duration(0.3)
|
||||
self.set_robot_mode(True)
|
||||
self.log("已设置为快速机器人模式")
|
||||
|
||||
def set_smooth_mode(self):
|
||||
"""预设:平滑模式(非机器人)"""
|
||||
self.set_robot_mode(False)
|
||||
self.set_rotation_speed(30.0)
|
||||
self.log("已设置为平滑旋转模式")
|
||||
@ -1,215 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
旋转脚本 - 让对象持续旋转
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
|
||||
class RotatorScript(ScriptBase):
|
||||
"""旋转脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒)
|
||||
self.max_angle = 25.0 # 最大旋转角度(相对于初始角度)
|
||||
self.direction = 1
|
||||
self.current_offset = 0.0 # 当前相对于初始角度的偏移
|
||||
self.initial_angle = None # 模型的初始角度
|
||||
self.is_rotating = True # 是否正在旋转
|
||||
|
||||
# 机器人式停顿参数
|
||||
self.pause_duration = 0.5 # 停顿时间(秒)
|
||||
self.current_pause_time = 0.0 # 当前停顿计时
|
||||
self.is_paused = False # 是否正在停顿
|
||||
self.robot_mode = True # 是否启用机器人模式
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("机器人旋转脚本启动!")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"最大旋转角度: ±{self.max_angle}度")
|
||||
self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}")
|
||||
if self.robot_mode:
|
||||
self.log(f"停顿时间: {self.pause_duration}秒")
|
||||
|
||||
# 记录模型的初始角度
|
||||
if self.gameObject:
|
||||
initial_hpr = self.gameObject.getHpr()
|
||||
self.initial_angle = initial_hpr.getY() # 记录Y轴的初始角度(Pitch)
|
||||
self.log(f"模型初始角度: {self.initial_angle}度")
|
||||
self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
else:
|
||||
self.log("⚠️ 无法获取游戏对象,使用默认初始角度0")
|
||||
self.initial_angle = 0.0
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新 - 机器人式旋转"""
|
||||
if not self.is_rotating or self.initial_angle is None:
|
||||
return
|
||||
|
||||
# 如果正在停顿中
|
||||
if self.is_paused:
|
||||
self.current_pause_time += dt
|
||||
if self.current_pause_time >= self.pause_duration:
|
||||
# 停顿结束,继续旋转
|
||||
self.is_paused = False
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"停顿结束,继续旋转,方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
return
|
||||
|
||||
# 计算角度变化量
|
||||
delta_angle = self.rotation_speed_y * dt * self.direction
|
||||
self.current_offset += delta_angle
|
||||
|
||||
# 检查是否到达边界
|
||||
reached_boundary = False
|
||||
if self.current_offset > self.max_angle:
|
||||
self.current_offset = self.max_angle
|
||||
self.direction *= -1
|
||||
reached_boundary = True
|
||||
elif self.current_offset < -self.max_angle:
|
||||
self.current_offset = -self.max_angle
|
||||
self.direction *= -1
|
||||
reached_boundary = True
|
||||
|
||||
# 如果到达边界且启用机器人模式,开始停顿
|
||||
if reached_boundary and self.robot_mode:
|
||||
self.is_paused = True
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"到达边界 ({self.current_offset}°),开始停顿 {self.pause_duration}秒")
|
||||
|
||||
# 计算最终角度(初始角度 + 偏移量)
|
||||
final_angle = self.initial_angle + self.current_offset
|
||||
|
||||
# 设置新的旋转(只改变Y轴,保持其他不变)
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), final_angle, current_hpr.getZ())
|
||||
|
||||
# if not self.is_rotating:
|
||||
# return
|
||||
#
|
||||
# # 获取当前旋转并应用增量
|
||||
# current_hpr = self.gameObject.getHpr()
|
||||
# new_r = current_hpr.getZ() + self.rotation_speed_y * dt
|
||||
# self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("机器人旋转脚本停止")
|
||||
|
||||
# ==================== 机器人模式控制方法 ====================
|
||||
|
||||
def set_robot_mode(self, enabled=True):
|
||||
"""
|
||||
启用或禁用机器人模式
|
||||
|
||||
Args:
|
||||
enabled: 是否启用机器人模式
|
||||
"""
|
||||
self.robot_mode = enabled
|
||||
self.log(f"机器人模式: {'开启' if enabled else '关闭'}")
|
||||
if not enabled:
|
||||
self.is_paused = False # 如果禁用机器人模式,立即结束停顿
|
||||
|
||||
def set_pause_duration(self, duration):
|
||||
"""
|
||||
设置停顿时间
|
||||
|
||||
Args:
|
||||
duration: 停顿时间(秒)
|
||||
"""
|
||||
self.pause_duration = max(0.1, duration) # 最小0.1秒
|
||||
self.log(f"停顿时间已设置为: {self.pause_duration}秒")
|
||||
|
||||
def set_max_angle(self, new_max_angle):
|
||||
"""
|
||||
设置新的最大旋转角度
|
||||
|
||||
Args:
|
||||
new_max_angle: 新的最大角度值
|
||||
"""
|
||||
self.max_angle = new_max_angle
|
||||
self.log(f"最大旋转角度已设置为: ±{self.max_angle}度")
|
||||
if self.initial_angle is not None:
|
||||
self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
|
||||
def set_rotation_speed(self, new_speed):
|
||||
"""
|
||||
设置新的旋转速度
|
||||
|
||||
Args:
|
||||
new_speed: 新的旋转速度(度/秒)
|
||||
"""
|
||||
self.rotation_speed_y = new_speed
|
||||
self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒")
|
||||
|
||||
def pause_rotation(self):
|
||||
"""暂停旋转"""
|
||||
self.is_rotating = False
|
||||
self.log("旋转已暂停")
|
||||
|
||||
def resume_rotation(self):
|
||||
"""恢复旋转"""
|
||||
self.is_rotating = True
|
||||
self.is_paused = False # 同时结束停顿状态
|
||||
self.log("旋转已恢复")
|
||||
|
||||
def reset_to_initial_angle(self):
|
||||
"""重置到初始角度"""
|
||||
if self.initial_angle is not None and self.gameObject:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), self.initial_angle, current_hpr.getZ())
|
||||
self.current_offset = 0.0
|
||||
self.direction = 1
|
||||
self.is_paused = False
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"已重置到初始角度: {self.initial_angle}度")
|
||||
|
||||
def get_current_info(self):
|
||||
"""获取当前旋转信息"""
|
||||
if self.gameObject and self.initial_angle is not None:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
current_angle = current_hpr.getY()
|
||||
self.log("=== 机器人旋转信息 ===")
|
||||
self.log(f"初始角度: {self.initial_angle}度")
|
||||
self.log(f"当前角度: {current_angle}度")
|
||||
self.log(f"偏移量: {self.current_offset}度")
|
||||
self.log(f"最大角度: ±{self.max_angle}度")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}")
|
||||
self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}")
|
||||
if self.robot_mode:
|
||||
self.log(f"停顿时间: {self.pause_duration}秒")
|
||||
self.log(f"当前状态: {'停顿中' if self.is_paused else '旋转中'}")
|
||||
if self.is_paused:
|
||||
remaining_time = self.pause_duration - self.current_pause_time
|
||||
self.log(f"剩余停顿时间: {remaining_time:.1f}秒")
|
||||
self.log("=== 信息结束 ===")
|
||||
else:
|
||||
self.log("无法获取旋转信息")
|
||||
|
||||
# ==================== 预设配置方法 ====================
|
||||
|
||||
def set_slow_robot_mode(self):
|
||||
"""预设:慢速机器人模式"""
|
||||
self.set_rotation_speed(15.0)
|
||||
self.set_pause_duration(1.0)
|
||||
self.set_robot_mode(True)
|
||||
self.log("已设置为慢速机器人模式")
|
||||
|
||||
def set_fast_robot_mode(self):
|
||||
"""预设:快速机器人模式"""
|
||||
self.set_rotation_speed(45.0)
|
||||
self.set_pause_duration(0.3)
|
||||
self.set_robot_mode(True)
|
||||
self.log("已设置为快速机器人模式")
|
||||
|
||||
def set_smooth_mode(self):
|
||||
"""预设:平滑模式(非机器人)"""
|
||||
self.set_robot_mode(False)
|
||||
self.set_rotation_speed(30.0)
|
||||
self.log("已设置为平滑旋转模式")
|
||||
@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
旋转脚本 - 让对象持续旋转
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
|
||||
class RotatorScript(ScriptBase):
|
||||
"""旋转脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒)
|
||||
self.max_angle = 15.0
|
||||
self.direction = 1
|
||||
self.current_angle = 0.0
|
||||
self.is_rotating = True # 是否正在旋转
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("旋转脚本启动!")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
|
||||
delta_angle = self.rotation_speed_y * dt * self.direction
|
||||
self.current_angle += delta_angle
|
||||
|
||||
# 如果超出角度范围,则反向
|
||||
if self.current_angle > self.max_angle:
|
||||
self.current_angle = self.max_angle
|
||||
self.direction *= -1
|
||||
elif self.current_angle < -self.max_angle:
|
||||
self.current_angle = -self.max_angle
|
||||
self.direction *= -1
|
||||
|
||||
# 设置新的旋转(只改变Z轴,保持其他不变)
|
||||
base_hpr = self.gameObject.getHpr()
|
||||
new_r = self.current_angle
|
||||
self.gameObject.setHpr(base_hpr.getX(), base_hpr.getY(), new_r)
|
||||
|
||||
# if not self.is_rotating:
|
||||
# return
|
||||
#
|
||||
# # 获取当前旋转并应用增量
|
||||
# current_hpr = self.gameObject.getHpr()
|
||||
# new_r = current_hpr.getZ() + self.rotation_speed_y * dt
|
||||
# self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("旋转脚本停止")
|
||||
@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
旋转脚本 - 让对象持续旋转
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
class RotatorScript(ScriptBase):
|
||||
"""旋转脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒)
|
||||
self.is_rotating = True # 是否正在旋转
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("旋转脚本启动!")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
|
||||
def update(self, dt):
|
||||
# 检查 gameObject 是否存在且不为空
|
||||
if not self.gameObject or self.gameObject.isEmpty():
|
||||
print("RotatorScript: gameObject is empty or None, skipping update")
|
||||
return
|
||||
"""每帧更新"""
|
||||
if not self.is_rotating:
|
||||
return
|
||||
|
||||
# 获取当前旋转并应用增量
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
new_h = current_hpr.getX() + self.rotation_speed_y * dt
|
||||
self.gameObject.setHpr(new_h, current_hpr.getY(), current_hpr.getZ())
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("旋转脚本停止")
|
||||
@ -1,91 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
缩放脚本 - 让对象产生呼吸般的缩放效果
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
import math
|
||||
|
||||
class ScalerScript(ScriptBase):
|
||||
"""缩放脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 缩放参数
|
||||
self.base_scale = 1.0 # 基础缩放
|
||||
self.scale_amplitude = 0.3 # 缩放幅度
|
||||
self.scale_speed = 2.0 # 缩放速度 (周期/秒)
|
||||
self.uniform_scale = True # 是否统一缩放(所有轴)
|
||||
|
||||
# 内部变量
|
||||
self.time_accumulator = 0.0 # 时间累积器
|
||||
self.original_scale = None # 原始缩放
|
||||
self.is_scaling = True # 是否正在缩放
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("缩放脚本启动!")
|
||||
self.log(f"缩放参数: 基础={self.base_scale}, 幅度={self.scale_amplitude}, 速度={self.scale_speed}")
|
||||
|
||||
# 记录原始缩放
|
||||
self.original_scale = self.gameObject.getScale()
|
||||
self.log(f"原始缩放: {self.original_scale}")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
if not self.is_scaling:
|
||||
return
|
||||
|
||||
# 累积时间
|
||||
self.time_accumulator += dt
|
||||
|
||||
# 计算正弦波缩放值
|
||||
sine_value = math.sin(self.time_accumulator * self.scale_speed * 2 * math.pi)
|
||||
scale_factor = self.base_scale + (self.scale_amplitude * sine_value)
|
||||
|
||||
# 应用缩放
|
||||
if self.uniform_scale:
|
||||
# 统一缩放
|
||||
self.gameObject.setScale(scale_factor)
|
||||
else:
|
||||
# 非统一缩放(仅Z轴)
|
||||
current_scale = self.gameObject.getScale()
|
||||
self.gameObject.setScale(current_scale.getX(), current_scale.getY(), scale_factor)
|
||||
|
||||
def set_scale_parameters(self, base=None, amplitude=None, speed=None, uniform=None):
|
||||
"""设置缩放参数"""
|
||||
if base is not None:
|
||||
self.base_scale = base
|
||||
if amplitude is not None:
|
||||
self.scale_amplitude = amplitude
|
||||
if speed is not None:
|
||||
self.scale_speed = speed
|
||||
if uniform is not None:
|
||||
self.uniform_scale = uniform
|
||||
|
||||
self.log(f"缩放参数更新: 基础={self.base_scale}, 幅度={self.scale_amplitude}, 速度={self.scale_speed}")
|
||||
|
||||
def toggle_scaling(self):
|
||||
"""切换缩放状态"""
|
||||
self.is_scaling = not self.is_scaling
|
||||
status = "恢复" if self.is_scaling else "暂停"
|
||||
self.log(f"缩放{status}")
|
||||
|
||||
def reset_scale(self):
|
||||
"""重置到原始缩放"""
|
||||
if self.original_scale:
|
||||
self.gameObject.setScale(self.original_scale)
|
||||
self.time_accumulator = 0.0
|
||||
self.log("缩放已重置到原始值")
|
||||
|
||||
def pulse_once(self):
|
||||
"""执行一次脉冲缩放"""
|
||||
self.time_accumulator = 0.0
|
||||
self.log("执行脉冲缩放")
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("缩放脚本停止")
|
||||
@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
TestMover - 移动脚本
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
class Testmover(ScriptBase):
|
||||
"""移动脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.speed = 5.0 # 移动速度
|
||||
self.direction = [1, 0, 0] # 移动方向
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("移动脚本开始运行!")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
if self.transform:
|
||||
# 计算移动偏移
|
||||
offset_x = self.direction[0] * self.speed * dt
|
||||
offset_y = self.direction[1] * self.speed * dt
|
||||
offset_z = self.direction[2] * self.speed * dt
|
||||
|
||||
# 更新位置
|
||||
current_pos = self.transform.getPos()
|
||||
new_pos = (
|
||||
current_pos.x + offset_x,
|
||||
current_pos.y + offset_y,
|
||||
current_pos.z + offset_z
|
||||
)
|
||||
self.transform.setPos(*new_pos)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("移动脚本被销毁")
|
||||
@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
TestRotator - 自定义脚本
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
class Testrotator(ScriptBase):
|
||||
"""自定义脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# 在这里初始化您的变量
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("脚本开始运行!")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
# 在这里编写更新逻辑
|
||||
pass
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("脚本被销毁")
|
||||
@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
TestScaler - 自定义脚本
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
class Testscaler(ScriptBase):
|
||||
"""自定义脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# 在这里初始化您的变量
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("脚本开始运行!")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
# 在这里编写更新逻辑
|
||||
pass
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("脚本被销毁")
|
||||
@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
a - 自定义脚本
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
class A(ScriptBase):
|
||||
"""自定义脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# 在这里初始化您的变量
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("脚本开始运行!")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
# 在这里编写更新逻辑
|
||||
pass
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("脚本被销毁")
|
||||
@ -1,47 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
示例脚本 - 演示如何编写脚本
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
class ExampleScript(ScriptBase):
|
||||
"""示例脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.counter = 0
|
||||
self.rotation_speed = 30.0 # 度/秒
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("示例脚本开始运行!")
|
||||
self.log(f"挂载到对象: {self.gameObject.getName()}")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
self.counter += 1
|
||||
|
||||
# 每60帧输出一次信息
|
||||
if self.counter % 60 == 0:
|
||||
self.log(f"运行了 {self.counter} 帧")
|
||||
|
||||
# 让对象旋转
|
||||
if self.transform:
|
||||
current_h = self.transform.getH()
|
||||
new_h = current_h + self.rotation_speed * dt
|
||||
self.transform.setH(new_h)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("示例脚本被销毁")
|
||||
|
||||
def on_enable(self):
|
||||
"""脚本启用时调用"""
|
||||
self.log("示例脚本被启用")
|
||||
|
||||
def on_disable(self):
|
||||
"""脚本禁用时调用"""
|
||||
self.log("示例脚本被禁用")
|
||||
@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
test_quick_script - 自定义脚本
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
class TestQuickScript(ScriptBase):
|
||||
"""自定义脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# 在这里初始化您的变量
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("脚本开始运行!")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
# 在这里编写更新逻辑
|
||||
pass
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("脚本被销毁")
|
||||
@ -1,534 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""EG packaged project runtime template."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from direct.actor.Actor import Actor
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import (
|
||||
CardMaker,
|
||||
Filename,
|
||||
MovieTexture,
|
||||
Point3,
|
||||
TextNode,
|
||||
Texture,
|
||||
TransparencyAttrib,
|
||||
Vec3,
|
||||
load_prc_file_data,
|
||||
)
|
||||
|
||||
PROJECT_NAME = "111"
|
||||
|
||||
|
||||
def _bootstrap_paths():
|
||||
if getattr(sys, "frozen", False):
|
||||
project_root = os.path.dirname(sys.executable)
|
||||
else:
|
||||
project_root = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
os.chdir(project_root)
|
||||
|
||||
search_paths = [
|
||||
project_root,
|
||||
os.path.join(project_root, "third_party"),
|
||||
os.path.join(project_root, "RenderPipelineFile"),
|
||||
]
|
||||
for path in search_paths:
|
||||
if os.path.isdir(path) and path not in sys.path:
|
||||
sys.path.insert(0, path)
|
||||
|
||||
return project_root
|
||||
|
||||
|
||||
PROJECT_ROOT = _bootstrap_paths()
|
||||
|
||||
|
||||
class MainApp(ShowBase):
|
||||
def __init__(self):
|
||||
self.project_path = PROJECT_ROOT
|
||||
self.gui_elements = []
|
||||
self.chinese_font = None
|
||||
self.script_manager = None
|
||||
self.render_pipeline = None
|
||||
|
||||
load_prc_file_data(
|
||||
"",
|
||||
f"""
|
||||
win-size 1380 750
|
||||
window-title {PROJECT_NAME}
|
||||
sync-video false
|
||||
show-frame-rate-meter false
|
||||
support-threads false
|
||||
""",
|
||||
)
|
||||
|
||||
from rpcore import RenderPipeline
|
||||
|
||||
self.render_pipeline = RenderPipeline()
|
||||
self.render_pipeline.pre_showbase_init()
|
||||
ShowBase.__init__(self)
|
||||
self.render_pipeline.create(self)
|
||||
self.render_pipeline._showbase.camera = self.render_pipeline._showbase.cam
|
||||
self.disableMouse()
|
||||
|
||||
self._load_font()
|
||||
self._init_script_manager()
|
||||
self.load_full_scene()
|
||||
self.load_gui_from_json()
|
||||
|
||||
def _init_script_manager(self):
|
||||
script_module = importlib.import_module("core.script_system")
|
||||
self.script_manager = script_module.ScriptManager(self)
|
||||
self.script_manager.hot_reload_enabled = False
|
||||
|
||||
scripts_dir = self.get_resource_path("scripts")
|
||||
if hasattr(self.script_manager, "set_scripts_directory"):
|
||||
self.script_manager.set_scripts_directory(
|
||||
scripts_dir,
|
||||
create=False,
|
||||
reload_scripts=False,
|
||||
)
|
||||
|
||||
self.script_manager.start_system()
|
||||
self.script_manager.set_hot_reload_enabled(False)
|
||||
|
||||
def _load_font(self):
|
||||
font_candidates = [
|
||||
"C:/Windows/Fonts/msyh.ttc",
|
||||
"C:/Windows/Fonts/simhei.ttf",
|
||||
]
|
||||
for font_path in font_candidates:
|
||||
if os.path.exists(font_path):
|
||||
try:
|
||||
self.chinese_font = self.loader.loadFont(font_path)
|
||||
if self.chinese_font:
|
||||
print(f"✓ 中文字体加载成功: {font_path}")
|
||||
return
|
||||
except Exception:
|
||||
continue
|
||||
print("⚠ 未找到可用中文字体,继续使用默认字体")
|
||||
|
||||
def get_chinese_font(self):
|
||||
return self.chinese_font
|
||||
|
||||
def get_resource_path(self, relative_path):
|
||||
return os.path.normpath(os.path.join(PROJECT_ROOT, relative_path))
|
||||
|
||||
def _resolve_media_path(self, relative_path):
|
||||
if not relative_path:
|
||||
return ""
|
||||
if os.path.isabs(relative_path):
|
||||
return relative_path
|
||||
return self.get_resource_path(relative_path.replace("/", os.sep))
|
||||
|
||||
def load_full_scene(self):
|
||||
scene_file = self.get_resource_path("scene.bam")
|
||||
if not os.path.exists(scene_file):
|
||||
print(f"⚠ 未找到场景文件: {scene_file}")
|
||||
return
|
||||
|
||||
scene = self.loader.loadModel(Filename.fromOsSpecific(scene_file))
|
||||
if not scene:
|
||||
print("⚠ 场景文件加载失败")
|
||||
return
|
||||
|
||||
scene.reparentTo(self.render)
|
||||
self.render_pipeline.prepare_scene(scene)
|
||||
self.process_scene_elements(scene)
|
||||
print("✓ 场景加载完成")
|
||||
|
||||
def process_scene_elements(self, root_node):
|
||||
processed_lights = set()
|
||||
|
||||
def walk(node_path):
|
||||
self._apply_user_visibility(node_path)
|
||||
|
||||
if node_path.hasTag("scripts_info"):
|
||||
try:
|
||||
scripts_info = json.loads(node_path.getTag("scripts_info"))
|
||||
self.process_scripts(node_path, scripts_info)
|
||||
except Exception as e:
|
||||
print(f"处理节点脚本失败 {node_path.getName()}: {e}")
|
||||
|
||||
if node_path.hasTag("light_type") and node_path not in processed_lights:
|
||||
if node_path.hasTag("is_auxiliary_light") and node_path.getTag("is_auxiliary_light").lower() == "true":
|
||||
return
|
||||
|
||||
light_type = node_path.getTag("light_type")
|
||||
if light_type == "spot_light":
|
||||
self._recreate_spot_light(node_path)
|
||||
elif light_type == "point_light":
|
||||
self._recreate_point_light(node_path)
|
||||
processed_lights.add(node_path)
|
||||
|
||||
for child in node_path.getChildren():
|
||||
walk(child)
|
||||
|
||||
walk(root_node)
|
||||
|
||||
def _apply_user_visibility(self, node_path):
|
||||
if not node_path.hasTag("user_visible"):
|
||||
return
|
||||
|
||||
user_visible = node_path.getTag("user_visible").lower() == "true"
|
||||
node_path.setPythonTag("user_visible", user_visible)
|
||||
if user_visible:
|
||||
node_path.show()
|
||||
else:
|
||||
node_path.hide()
|
||||
|
||||
def _recreate_spot_light(self, light_node):
|
||||
try:
|
||||
from rpcore import SpotLight
|
||||
|
||||
light = SpotLight()
|
||||
light.direction = Vec3(0, 0, -1)
|
||||
light.fov = float(light_node.getTag("light_fov")) if light_node.hasTag("light_fov") else 70.0
|
||||
light.energy = float(light_node.getTag("light_energy")) if light_node.hasTag("light_energy") else 5000.0
|
||||
light.radius = float(light_node.getTag("light_radius")) if light_node.hasTag("light_radius") else 1000.0
|
||||
light.casts_shadows = True
|
||||
light.shadow_map_resolution = 256
|
||||
light.setPos(light_node.getPos())
|
||||
self.render_pipeline.add_light(light)
|
||||
except Exception as e:
|
||||
print(f"创建聚光灯失败 {light_node.getName()}: {e}")
|
||||
|
||||
def _recreate_point_light(self, light_node):
|
||||
try:
|
||||
from rpcore import PointLight
|
||||
|
||||
light = PointLight()
|
||||
light.energy = float(light_node.getTag("light_energy")) if light_node.hasTag("light_energy") else 5000.0
|
||||
light.radius = float(light_node.getTag("light_radius")) if light_node.hasTag("light_radius") else 1000.0
|
||||
light.inner_radius = 0.4
|
||||
light.casts_shadows = True
|
||||
light.shadow_map_resolution = 256
|
||||
light.setPos(light_node.getPos())
|
||||
self.render_pipeline.add_light(light)
|
||||
except Exception as e:
|
||||
print(f"创建点光源失败 {light_node.getName()}: {e}")
|
||||
|
||||
def process_scripts(self, node_path, script_info_list):
|
||||
if not self.script_manager:
|
||||
return
|
||||
|
||||
for script_info in script_info_list or []:
|
||||
script_name = str(script_info.get("name", "") or "").strip()
|
||||
if not script_name:
|
||||
continue
|
||||
|
||||
try:
|
||||
if script_name not in self.script_manager.loader.script_classes:
|
||||
script_path = ""
|
||||
if hasattr(self.script_manager, "resolve_script_path"):
|
||||
script_path = self.script_manager.resolve_script_path(script_info)
|
||||
if script_path:
|
||||
self.script_manager.load_script_from_file(script_path)
|
||||
|
||||
script_component = self.script_manager.add_script_to_object(node_path, script_name)
|
||||
if script_component:
|
||||
print(f"✓ 脚本 {script_name} 已挂载到 {node_path.getName()}")
|
||||
else:
|
||||
print(f"⚠ 脚本 {script_name} 挂载失败")
|
||||
except Exception as e:
|
||||
print(f"挂载脚本失败 {script_name}: {e}")
|
||||
|
||||
def load_gui_from_json(self):
|
||||
gui_json_path = self.get_resource_path(os.path.join("gui", "gui_elements.json"))
|
||||
if not os.path.exists(gui_json_path):
|
||||
return
|
||||
|
||||
with open(gui_json_path, "r", encoding="utf-8") as f:
|
||||
content = f.read().strip()
|
||||
if not content:
|
||||
return
|
||||
|
||||
gui_data = json.loads(content)
|
||||
self.create_gui_elements(gui_data)
|
||||
|
||||
def create_gui_elements(self, element_data):
|
||||
processed_names = set()
|
||||
element_original_data = {}
|
||||
|
||||
for index, gui_info in enumerate(element_data or []):
|
||||
name = gui_info.get("name", f"gui_element_{index}")
|
||||
element_original_data[name] = {
|
||||
"scale": gui_info.get("scale", [1, 1, 1]),
|
||||
"position": gui_info.get("position", [0, 0, 0]),
|
||||
"parent_name": gui_info.get("parent_name"),
|
||||
}
|
||||
|
||||
for index, gui_info in enumerate(element_data or []):
|
||||
try:
|
||||
gui_type = gui_info.get("type", "unknown")
|
||||
name = gui_info.get("name", f"gui_element_{index}")
|
||||
if name in processed_names:
|
||||
continue
|
||||
processed_names.add(name)
|
||||
|
||||
position = list(gui_info.get("position", [0, 0, 0]))
|
||||
scale = list(gui_info.get("scale", [1, 1, 1]))
|
||||
parent_name = gui_info.get("parent_name")
|
||||
|
||||
if parent_name and parent_name in element_original_data:
|
||||
parent_scale = element_original_data[parent_name]["scale"]
|
||||
for i in range(min(len(position), len(parent_scale))):
|
||||
position[i] *= parent_scale[i]
|
||||
for i in range(min(len(scale), len(parent_scale))):
|
||||
scale[i] *= parent_scale[i]
|
||||
|
||||
text = gui_info.get("text", "")
|
||||
image_path = self._resolve_media_path(gui_info.get("image_path", ""))
|
||||
video_path = self._resolve_media_path(gui_info.get("video_path", ""))
|
||||
|
||||
new_element = None
|
||||
if gui_type == "3d_text":
|
||||
new_element = self.create_gui_3d_text(tuple(position), text, scale[0] if scale else 1.0)
|
||||
elif gui_type == "3d_image":
|
||||
new_element = self.create_gui_3d_image(tuple(position), image_path, scale)
|
||||
elif gui_type == "button":
|
||||
new_element = self.create_gui_button(tuple(position), text, scale[0] if scale else 1.0)
|
||||
elif gui_type == "label":
|
||||
new_element = self.create_gui_label(tuple(position), text, scale[0] if scale else 1.0)
|
||||
elif gui_type == "entry":
|
||||
new_element = self.create_gui_entry(tuple(position), text, scale[0] if scale else 1.0)
|
||||
elif gui_type == "2d_image":
|
||||
new_element = self.create_gui_2d_image(tuple(position), image_path, scale)
|
||||
elif gui_type == "video_screen":
|
||||
new_element = self.create_gui_video_screen(tuple(position), scale, video_path)
|
||||
elif gui_type == "2d_video_screen":
|
||||
new_element = self.create_gui_2d_video_screen(tuple(position), scale, video_path)
|
||||
|
||||
if not new_element:
|
||||
continue
|
||||
|
||||
self._apply_gui_metadata(new_element, gui_info, gui_type, text, image_path, video_path)
|
||||
self.gui_elements.append(new_element)
|
||||
|
||||
if gui_info.get("scripts"):
|
||||
self.process_scripts(new_element, gui_info["scripts"])
|
||||
except Exception as e:
|
||||
print(f"重建 GUI 元素失败: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
def _apply_gui_metadata(self, node, gui_info, gui_type, text, image_path, video_path):
|
||||
try:
|
||||
name = gui_info.get("name")
|
||||
if name and hasattr(node, "setName"):
|
||||
node.setName(name)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if hasattr(node, "setTag"):
|
||||
node.setTag("gui_type", gui_type)
|
||||
node.setTag("is_gui_element", "true")
|
||||
if text:
|
||||
node.setTag("gui_text", str(text))
|
||||
if image_path:
|
||||
node.setTag("gui_image_path", str(image_path))
|
||||
if video_path:
|
||||
node.setTag("video_path", str(video_path))
|
||||
for key, value in (gui_info.get("tags") or {}).items():
|
||||
try:
|
||||
node.setTag(str(key), str(value))
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
user_visible = bool(gui_info.get("user_visible", True))
|
||||
if hasattr(node, "setPythonTag"):
|
||||
node.setPythonTag("user_visible", user_visible)
|
||||
if hasattr(node, "show") and hasattr(node, "hide"):
|
||||
if user_visible:
|
||||
node.show()
|
||||
else:
|
||||
node.hide()
|
||||
|
||||
def create_gui_button(self, pos=(0, 0, 0), text="按钮", size=0.1):
|
||||
from direct.gui.DirectGui import DirectButton
|
||||
|
||||
return DirectButton(
|
||||
text=text,
|
||||
pos=(pos[0], pos[1], pos[2]),
|
||||
scale=size,
|
||||
frameColor=(0.2, 0.6, 0.8, 1),
|
||||
text_font=self.get_chinese_font() or None,
|
||||
rolloverSound=None,
|
||||
clickSound=None,
|
||||
parent=self.aspect2d,
|
||||
command=None,
|
||||
)
|
||||
|
||||
def create_gui_label(self, pos=(0, 0, 0), text="标签", size=0.08):
|
||||
from direct.gui.DirectGui import DirectLabel
|
||||
|
||||
return DirectLabel(
|
||||
text=text,
|
||||
pos=(pos[0], pos[1], pos[2]),
|
||||
scale=size,
|
||||
frameColor=(0, 0, 0, 0),
|
||||
text_fg=(1, 1, 1, 1),
|
||||
text_font=self.get_chinese_font() or None,
|
||||
text_align=TextNode.ACenter,
|
||||
text_mayChange=True,
|
||||
parent=self.aspect2d,
|
||||
)
|
||||
|
||||
def create_gui_entry(self, pos=(0, 0, 0), placeholder="输入文本...", size=0.08):
|
||||
from direct.gui.DirectGui import DirectEntry
|
||||
|
||||
return DirectEntry(
|
||||
text="",
|
||||
pos=(pos[0], pos[1], pos[2]),
|
||||
scale=size,
|
||||
command=self.on_gui_entry_submit,
|
||||
initialText=placeholder,
|
||||
numLines=1,
|
||||
width=12,
|
||||
focus=0,
|
||||
frameColor=(0, 0, 0, 0),
|
||||
text_fg=(1, 1, 1, 1),
|
||||
text_font=self.get_chinese_font() or None,
|
||||
text_align=TextNode.ACenter,
|
||||
text_mayChange=True,
|
||||
parent=self.aspect2d,
|
||||
rolloverSound=None,
|
||||
clickSound=None,
|
||||
suppressKeys=True,
|
||||
suppressMouse=True,
|
||||
)
|
||||
|
||||
def on_gui_entry_submit(self, text, *_args):
|
||||
print(f"GUI 输入框提交: {text}")
|
||||
|
||||
def create_gui_2d_image(self, pos=(0, 0, 0), image_path="", size=(1, 1, 1)):
|
||||
if isinstance(size, (list, tuple)) and len(size) >= 3:
|
||||
width_scale = float(size[0]) * 0.2
|
||||
height_scale = float(size[2]) * 0.2
|
||||
else:
|
||||
scalar = float(size) if isinstance(size, (int, float)) else 0.2
|
||||
width_scale = scalar * 0.1
|
||||
height_scale = width_scale
|
||||
|
||||
cm = CardMaker("gui_2d_image")
|
||||
cm.setFrame(-width_scale, width_scale, -height_scale, height_scale)
|
||||
image_node = self.aspect2d.attachNewNode(cm.generate())
|
||||
image_node.setPos(pos)
|
||||
image_node.setBin("fixed", 0)
|
||||
image_node.setDepthWrite(False)
|
||||
image_node.setDepthTest(False)
|
||||
image_node.setTransparency(TransparencyAttrib.MAlpha)
|
||||
if image_path:
|
||||
texture = self.loader.loadTexture(image_path)
|
||||
if texture:
|
||||
image_node.setTexture(texture, 1)
|
||||
return image_node
|
||||
|
||||
def create_gui_3d_text(self, pos=(0, 0, 0), text="3D文本", size=0.5):
|
||||
text_node = TextNode("gui_3d_text")
|
||||
text_node.setText(text)
|
||||
text_node.setAlign(TextNode.ACenter)
|
||||
if self.get_chinese_font():
|
||||
text_node.setFont(self.get_chinese_font())
|
||||
|
||||
text_np = self.render.attachNewNode(text_node)
|
||||
text_np.setPos(Vec3(pos[0], pos[1], pos[2]))
|
||||
text_np.setScale(size)
|
||||
text_np.setBin("fixed", 40)
|
||||
text_np.setDepthWrite(False)
|
||||
return text_np
|
||||
|
||||
def create_gui_3d_image(self, pos=(0, 0, 0), image_path="", size=(1, 1, 1)):
|
||||
if isinstance(size, (list, tuple)) and len(size) >= 3:
|
||||
width_scale = float(size[0])
|
||||
height_scale = float(size[2])
|
||||
else:
|
||||
width_scale = float(size) if isinstance(size, (int, float)) else 1.0
|
||||
height_scale = width_scale
|
||||
|
||||
cm = CardMaker("gui_3d_image")
|
||||
cm.setFrame(-width_scale, width_scale, -height_scale, height_scale)
|
||||
image_node = self.render.attachNewNode(cm.generate())
|
||||
image_node.setPos(pos)
|
||||
image_node.setTransparency(TransparencyAttrib.MAlpha)
|
||||
if image_path:
|
||||
texture = self.loader.loadTexture(image_path)
|
||||
if texture:
|
||||
image_node.setTexture(texture, 1)
|
||||
return image_node
|
||||
|
||||
def _load_movie_texture(self, name, video_path):
|
||||
if not video_path:
|
||||
return None
|
||||
movie_texture = MovieTexture(name)
|
||||
if not movie_texture.read(Filename.fromOsSpecific(video_path)):
|
||||
print(f"⚠ 无法加载视频: {video_path}")
|
||||
return None
|
||||
movie_texture.play()
|
||||
return movie_texture
|
||||
|
||||
def create_gui_video_screen(self, pos=(0, 0, 0), size=(1, 1, 1), video_path=""):
|
||||
if isinstance(size, (list, tuple)) and len(size) >= 3:
|
||||
width_scale = float(size[0])
|
||||
height_scale = float(size[2])
|
||||
else:
|
||||
width_scale = float(size) if isinstance(size, (int, float)) else 1.0
|
||||
height_scale = width_scale
|
||||
|
||||
cm = CardMaker("gui_video_screen")
|
||||
cm.setFrame(-width_scale, width_scale, -height_scale, height_scale)
|
||||
video_node = self.render.attachNewNode(cm.generate())
|
||||
video_node.setPos(pos)
|
||||
video_node.setTransparency(TransparencyAttrib.MAlpha)
|
||||
|
||||
movie_texture = self._load_movie_texture("gui_video_texture_3d", video_path)
|
||||
if movie_texture:
|
||||
video_node.setTexture(movie_texture, 1)
|
||||
return video_node
|
||||
|
||||
def create_gui_2d_video_screen(self, pos=(0, 0, 0), size=(1, 1, 1), video_path=""):
|
||||
if isinstance(size, (list, tuple)) and len(size) >= 3:
|
||||
width_scale = float(size[0]) * 0.2
|
||||
height_scale = float(size[2]) * 0.2
|
||||
else:
|
||||
scalar = float(size) if isinstance(size, (int, float)) else 0.2
|
||||
width_scale = scalar * 0.1
|
||||
height_scale = width_scale
|
||||
|
||||
cm = CardMaker("gui_2d_video_screen")
|
||||
cm.setFrame(-width_scale, width_scale, -height_scale, height_scale)
|
||||
video_node = self.aspect2d.attachNewNode(cm.generate())
|
||||
video_node.setPos(pos)
|
||||
video_node.setTransparency(TransparencyAttrib.MAlpha)
|
||||
video_node.setDepthWrite(False)
|
||||
video_node.setDepthTest(False)
|
||||
|
||||
movie_texture = self._load_movie_texture("gui_video_texture_2d", video_path)
|
||||
if movie_texture:
|
||||
video_node.setTexture(movie_texture, 1)
|
||||
return video_node
|
||||
|
||||
def play_model_animation(self):
|
||||
actors = self.render.findAllMatches("**/+ActorNode")
|
||||
for actor_np in actors:
|
||||
actor_node = actor_np.node()
|
||||
if isinstance(actor_node, Actor):
|
||||
anim_names = actor_node.getAnimNames()
|
||||
if anim_names:
|
||||
actor_node.loop(anim_names[0])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
app = MainApp()
|
||||
app.run()
|
||||
except Exception as e:
|
||||
print(f"应用程序启动失败: {e}")
|
||||
traceback.print_exc()
|
||||
15
imgui.ini
15
imgui.ini
@ -36,14 +36,14 @@ Collapsed=0
|
||||
DockId=0x00000007,0
|
||||
|
||||
[Window][属性面板]
|
||||
Pos=1502,20
|
||||
Pos=1504,20
|
||||
Size=346,996
|
||||
Collapsed=0
|
||||
DockId=0x00000002,0
|
||||
|
||||
[Window][控制台]
|
||||
Pos=341,617
|
||||
Size=1159,399
|
||||
Size=1161,399
|
||||
Collapsed=0
|
||||
DockId=0x00000006,1
|
||||
|
||||
@ -59,7 +59,7 @@ Collapsed=0
|
||||
|
||||
[Window][WindowOverViewport_11111111]
|
||||
Pos=0,20
|
||||
Size=1848,996
|
||||
Size=1850,996
|
||||
Collapsed=0
|
||||
|
||||
[Window][测试窗口1]
|
||||
@ -83,12 +83,12 @@ Size=400,300
|
||||
Collapsed=0
|
||||
|
||||
[Window][选择路径]
|
||||
Pos=624,258
|
||||
Pos=625,258
|
||||
Size=600,500
|
||||
Collapsed=0
|
||||
|
||||
[Window][打开项目]
|
||||
Pos=674,308
|
||||
Pos=675,308
|
||||
Size=500,400
|
||||
Collapsed=0
|
||||
|
||||
@ -99,7 +99,7 @@ Collapsed=0
|
||||
|
||||
[Window][资源管理器]
|
||||
Pos=341,617
|
||||
Size=1159,399
|
||||
Size=1161,399
|
||||
Collapsed=0
|
||||
DockId=0x00000006,0
|
||||
|
||||
@ -203,7 +203,6 @@ Collapsed=0
|
||||
Pos=1438,20
|
||||
Size=610,748
|
||||
Collapsed=0
|
||||
DockId=0x00000005,2
|
||||
|
||||
[Window][项目另存为]
|
||||
Pos=794,432
|
||||
@ -226,7 +225,7 @@ Size=460,260
|
||||
Collapsed=0
|
||||
|
||||
[Docking][Data]
|
||||
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=1848,996 Split=X
|
||||
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=1850,996 Split=X
|
||||
DockNode ID=0x00000001 Parent=0x08BD597D SizeRef=2212,1012 Split=X
|
||||
DockNode ID=0x00000007 Parent=0x00000001 SizeRef=339,1084 Selected=0xE0015051
|
||||
DockNode ID=0x00000008 Parent=0x00000001 SizeRef=1871,1084 Split=Y
|
||||
|
||||
180
packaging/eg_editor.spec
Normal file
180
packaging/eg_editor.spec
Normal file
@ -0,0 +1,180 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
import importlib.machinery
|
||||
import importlib.util
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PyInstaller.utils.hooks import collect_dynamic_libs, collect_submodules
|
||||
|
||||
|
||||
def _has_module(module_name):
|
||||
try:
|
||||
return importlib.util.find_spec(module_name) is not None
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _safe_collect_submodules(package_name):
|
||||
try:
|
||||
return collect_submodules(package_name)
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
def _safe_collect_dynamic_libs(package_name):
|
||||
try:
|
||||
return collect_dynamic_libs(package_name)
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
def _append_data_dir(datas, project_root, relative_path, bundle_target=None):
|
||||
source = project_root / relative_path
|
||||
if source.exists():
|
||||
datas.append((str(source), bundle_target or relative_path.replace("\\", "/")))
|
||||
|
||||
|
||||
def _append_package_data_dir(datas, package_name, relative_path, bundle_target):
|
||||
spec = importlib.util.find_spec(package_name)
|
||||
if not spec or not spec.origin:
|
||||
return
|
||||
package_root = Path(spec.origin).resolve().parent
|
||||
source = package_root / relative_path
|
||||
if source.exists():
|
||||
datas.append((str(source), bundle_target))
|
||||
|
||||
|
||||
SPEC_DIR = Path(SPEC).resolve().parent
|
||||
PROJECT_ROOT = SPEC_DIR.parent
|
||||
THIRD_PARTY_DIR = PROJECT_ROOT / "third_party"
|
||||
RENDER_PIPELINE_DIR = PROJECT_ROOT / "RenderPipelineFile"
|
||||
ENTRY_SCRIPT = PROJECT_ROOT / "Start_Run.py"
|
||||
|
||||
for candidate in (PROJECT_ROOT, THIRD_PARTY_DIR, RENDER_PIPELINE_DIR):
|
||||
candidate_str = str(candidate)
|
||||
if candidate.exists() and candidate_str not in sys.path:
|
||||
sys.path.insert(0, candidate_str)
|
||||
|
||||
pathex = [
|
||||
str(PROJECT_ROOT),
|
||||
str(THIRD_PARTY_DIR),
|
||||
str(RENDER_PIPELINE_DIR),
|
||||
]
|
||||
|
||||
datas = []
|
||||
for relative_path, bundle_target in (
|
||||
("Resources", "Resources"),
|
||||
("RenderPipelineFile", "RenderPipelineFile"),
|
||||
("config", "config"),
|
||||
("vr_actions", "vr_actions"),
|
||||
("icons", "icons"),
|
||||
("scripts", "scripts"),
|
||||
("templates", "templates"),
|
||||
("ui/Skins", "ui/Skins"),
|
||||
("ssbo_component/shaders", "ssbo_component/shaders"),
|
||||
("ssbo_component/effects", "ssbo_component/effects"),
|
||||
):
|
||||
_append_data_dir(datas, PROJECT_ROOT, relative_path, bundle_target)
|
||||
|
||||
imgui_ini = PROJECT_ROOT / "imgui.ini"
|
||||
if imgui_ini.exists():
|
||||
datas.append((str(imgui_ini), "."))
|
||||
|
||||
demo_dir = PROJECT_ROOT / "demo"
|
||||
if demo_dir.exists():
|
||||
datas.append((str(demo_dir), "demo"))
|
||||
|
||||
_append_package_data_dir(datas, "panda3d", "etc", "panda3d/etc")
|
||||
|
||||
binaries = []
|
||||
for extension_suffix in importlib.machinery.EXTENSION_SUFFIXES:
|
||||
local_lui_binary = PROJECT_ROOT / "ui" / f"lui{extension_suffix}"
|
||||
if local_lui_binary.exists():
|
||||
binaries.append((str(local_lui_binary), "ui"))
|
||||
break
|
||||
|
||||
for package_name in ("panda3d", "imgui_bundle"):
|
||||
binaries += _safe_collect_dynamic_libs(package_name)
|
||||
|
||||
if _has_module("openvr"):
|
||||
binaries += _safe_collect_dynamic_libs("openvr")
|
||||
|
||||
hiddenimports = sorted(
|
||||
set(
|
||||
[
|
||||
"pyassimp",
|
||||
"p3dimgui",
|
||||
"p3dimgui.backend",
|
||||
"p3dimgui.shaders",
|
||||
"rpcore",
|
||||
"rpplugins",
|
||||
"rplibs",
|
||||
"RenderPipelineFile.rpcore",
|
||||
"RenderPipelineFile.rpplugins",
|
||||
"RenderPipelineFile.rplibs",
|
||||
]
|
||||
+ _safe_collect_submodules("core")
|
||||
+ _safe_collect_submodules("scene")
|
||||
+ _safe_collect_submodules("project")
|
||||
+ _safe_collect_submodules("ui")
|
||||
+ _safe_collect_submodules("ssbo_component")
|
||||
+ _safe_collect_submodules("TransformGizmo")
|
||||
+ _safe_collect_submodules("p3dimgui")
|
||||
+ _safe_collect_submodules("rpcore")
|
||||
+ _safe_collect_submodules("rpplugins")
|
||||
+ _safe_collect_submodules("rplibs")
|
||||
+ _safe_collect_submodules("RenderPipelineFile.rpcore")
|
||||
+ _safe_collect_submodules("RenderPipelineFile.rpplugins")
|
||||
+ _safe_collect_submodules("RenderPipelineFile.rplibs")
|
||||
+ (["openvr"] if _has_module("openvr") else [])
|
||||
+ (["pyassimp"] if _has_module("pyassimp") else [])
|
||||
)
|
||||
)
|
||||
|
||||
excludes = [
|
||||
"PyQt5",
|
||||
"PyQt6",
|
||||
"PySide2",
|
||||
"PySide6",
|
||||
"tkinter",
|
||||
]
|
||||
|
||||
a = Analysis(
|
||||
[str(ENTRY_SCRIPT)],
|
||||
pathex=pathex,
|
||||
binaries=binaries,
|
||||
datas=datas,
|
||||
hiddenimports=hiddenimports,
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=excludes,
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name="EGEditor",
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=False,
|
||||
console=False,
|
||||
)
|
||||
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=False,
|
||||
upx_exclude=[],
|
||||
name="EGEditor",
|
||||
)
|
||||
@ -1,90 +0,0 @@
|
||||
# 项目部署指南
|
||||
|
||||
## 🚀 在新电脑上运行此项目
|
||||
|
||||
### 方法1:使用Conda(推荐)
|
||||
|
||||
```bash
|
||||
# 1. 安装Miniconda
|
||||
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
|
||||
bash Miniconda3-latest-Linux-x86_64.sh
|
||||
|
||||
# 2. 克隆项目
|
||||
git clone <your-repo-url>
|
||||
cd eg
|
||||
|
||||
# 3. 创建conda环境
|
||||
conda env create -f environment.yml
|
||||
|
||||
# 4. 激活环境
|
||||
conda activate eg-project
|
||||
|
||||
# 5. 运行项目
|
||||
python Start_Run.py
|
||||
```
|
||||
|
||||
### 方法2:使用virtualenv + pip
|
||||
|
||||
```bash
|
||||
# 1. 克隆项目
|
||||
git clone <your-repo-url>
|
||||
cd eg
|
||||
|
||||
# 2. 创建虚拟环境
|
||||
python -m venv venv
|
||||
source venv/bin/activate # Linux/Mac
|
||||
# 或 venv\Scripts\activate # Windows
|
||||
|
||||
# 3. 安装依赖
|
||||
pip install -r requirements/requirements.txt
|
||||
|
||||
# 4. 运行项目
|
||||
python Start_Run.py
|
||||
```
|
||||
|
||||
## 📁 需要复制的文件
|
||||
|
||||
**必须复制:**
|
||||
- `main.py` - 主程序
|
||||
- `environment.yml` - Conda环境配置
|
||||
- `requirements.txt` - 核心依赖
|
||||
|
||||
**不要复制:**
|
||||
- `.conda/` - 虚拟环境文件夹
|
||||
- `__pycache__/` - Python缓存
|
||||
- `conda-requirements.txt` - 仅用于最小 Conda 环境说明
|
||||
|
||||
## 🔧 Cursor IDE 设置
|
||||
|
||||
1. **打开项目**:在Cursor中打开项目文件夹
|
||||
2. **选择解释器**:
|
||||
- `Ctrl+Shift+P` → "Python: Select Interpreter"
|
||||
- 选择虚拟环境中的Python路径
|
||||
3. **验证环境**:检查状态栏显示正确的Python版本
|
||||
|
||||
## 📦 项目依赖说明
|
||||
|
||||
- **Panda3D**: 3D图形引擎
|
||||
- **imgui-bundle / p3dimgui**: ImGui 编辑器界面与 Panda3D 桥接
|
||||
- **Pillow**: 图像处理与网页截图纹理转换
|
||||
- **numpy**: VR / SSBO 数值处理
|
||||
- **aiohttp**: 异步资源与场景 IO
|
||||
- **openvr**: VR 支持
|
||||
- **pyassimp**: 模型转换辅助
|
||||
|
||||
## 🌍 跨平台注意事项
|
||||
|
||||
- Linux/Mac: 使用 `source venv/bin/activate`
|
||||
- Windows: 使用 `venv\Scripts\activate`
|
||||
- 某些依赖可能需要系统级安装(如OpenGL库)
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
**Q: 无法安装Panda3D?**
|
||||
A: 确保系统有OpenGL支持,或使用conda安装
|
||||
|
||||
**Q: ImGui界面显示异常?**
|
||||
A: 检查显卡驱动/OpenGL支持,并确认已安装 `imgui-bundle`
|
||||
|
||||
**Q: 导入错误?**
|
||||
A: 检查 Python 版本,当前项目统一使用 Python 3.11。
|
||||
@ -1,3 +0,0 @@
|
||||
# Minimal conda spec for the current project environment.
|
||||
python=3.11
|
||||
pip
|
||||
@ -1,15 +0,0 @@
|
||||
name: eg-project
|
||||
channels:
|
||||
- conda-forge
|
||||
- defaults
|
||||
dependencies:
|
||||
- python=3.11
|
||||
- pip
|
||||
- pip:
|
||||
- Panda3D==1.10.15
|
||||
- imgui-bundle
|
||||
- Pillow>=9.0.1
|
||||
- numpy>=1.24
|
||||
- aiohttp>=3.9
|
||||
- openvr==2.2.0
|
||||
- pyassimp>=5.2.5
|
||||
Loading…
Reference in New Issue
Block a user