├── Docs └── Images │ ├── assetdatabase_project_folder.jpg │ ├── build_all.png │ ├── build_all_2.jpg │ ├── builtin_windows.jpg │ ├── compatibility_mode.jpg │ ├── component_gameobject_material_editors.jpg │ ├── context_menu.jpg │ ├── custom_external_asset_loader.jpg │ ├── custom_window_1.jpg │ ├── custom_window_2.jpg │ ├── custom_window_3.jpg │ ├── enumerators_folder.png │ ├── example_scenes_1.jpg │ ├── example_scenes_2.jpg │ ├── expose_to_editor.jpg │ ├── extending_window_1.jpg │ ├── file_importers_1.jpg │ ├── file_importers_2.jpg │ ├── generate_surrogate.png │ ├── getting_started_1.png │ ├── getting_started_2.png │ ├── getting_started_3.jpg │ ├── getting_started_4.jpg │ ├── getting_started_5.jpg │ ├── hierarchy_root.jpg │ ├── import_source_addressable_1.jpg │ ├── import_source_addressable_2.jpg │ ├── import_source_addressable_3.jpg │ ├── import_source_addressable_3.png │ ├── import_sources_1.jpg │ ├── import_sources_2.jpg │ ├── import_sources_3.jpg │ ├── import_sources_4.jpg │ ├── import_sources_5.jpg │ ├── inspector.png │ ├── inspector_configuration_1.jpg │ ├── inspector_configuration_window.jpg │ ├── instantiate_asset_example.jpg │ ├── jint_scripting_1.jpg │ ├── jint_scripting_2.jpg │ ├── jint_scripting_3.jpg │ ├── jint_scripting_4.jpg │ ├── jint_scripting_5.jpg │ ├── jint_scripting_6.jpg │ ├── jint_scripting_7.jpg │ ├── jint_scripting_8.jpg │ ├── jint_scripting_9.jpg │ ├── main_menu.jpg │ ├── manage_projects_example.jpg │ ├── manage_projects_example_2.jpg │ ├── modify_importsource_1.jpg │ ├── modify_importsource_2.jpg │ ├── modify_importsource_3.jpg │ ├── my_component.jpg │ ├── my_import_source_1.jpg │ ├── my_import_source_2.jpg │ ├── override_layout_1.jpg │ ├── override_layout_2.jpg │ ├── override_layout_3.jpg │ ├── override_scene_params.jpg │ ├── override_theme.jpg │ ├── override_tools.jpg │ ├── overview.png │ ├── persistent_data_path.jpg │ ├── property_editors.jpg │ ├── surrogates_folder.png │ ├── trilib_loader.jpg │ ├── ui-scale-after-override.png │ ├── ui-scale-before-override.png │ ├── universal_rp_1.jpg │ ├── universal_rp_2.jpg │ ├── universal_rp_3.jpg │ ├── use_zero_time_scale_in_edit_mode.jpg │ └── window.jpg ├── Misc └── MarkDownToHTML.md └── README.md /Docs/Images/assetdatabase_project_folder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/assetdatabase_project_folder.jpg -------------------------------------------------------------------------------- /Docs/Images/build_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/build_all.png -------------------------------------------------------------------------------- /Docs/Images/build_all_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/build_all_2.jpg -------------------------------------------------------------------------------- /Docs/Images/builtin_windows.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/builtin_windows.jpg -------------------------------------------------------------------------------- /Docs/Images/compatibility_mode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/compatibility_mode.jpg -------------------------------------------------------------------------------- /Docs/Images/component_gameobject_material_editors.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/component_gameobject_material_editors.jpg -------------------------------------------------------------------------------- /Docs/Images/context_menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/context_menu.jpg -------------------------------------------------------------------------------- /Docs/Images/custom_external_asset_loader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/custom_external_asset_loader.jpg -------------------------------------------------------------------------------- /Docs/Images/custom_window_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/custom_window_1.jpg -------------------------------------------------------------------------------- /Docs/Images/custom_window_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/custom_window_2.jpg -------------------------------------------------------------------------------- /Docs/Images/custom_window_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/custom_window_3.jpg -------------------------------------------------------------------------------- /Docs/Images/enumerators_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/enumerators_folder.png -------------------------------------------------------------------------------- /Docs/Images/example_scenes_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/example_scenes_1.jpg -------------------------------------------------------------------------------- /Docs/Images/example_scenes_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/example_scenes_2.jpg -------------------------------------------------------------------------------- /Docs/Images/expose_to_editor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/expose_to_editor.jpg -------------------------------------------------------------------------------- /Docs/Images/extending_window_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/extending_window_1.jpg -------------------------------------------------------------------------------- /Docs/Images/file_importers_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/file_importers_1.jpg -------------------------------------------------------------------------------- /Docs/Images/file_importers_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/file_importers_2.jpg -------------------------------------------------------------------------------- /Docs/Images/generate_surrogate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/generate_surrogate.png -------------------------------------------------------------------------------- /Docs/Images/getting_started_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/getting_started_1.png -------------------------------------------------------------------------------- /Docs/Images/getting_started_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/getting_started_2.png -------------------------------------------------------------------------------- /Docs/Images/getting_started_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/getting_started_3.jpg -------------------------------------------------------------------------------- /Docs/Images/getting_started_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/getting_started_4.jpg -------------------------------------------------------------------------------- /Docs/Images/getting_started_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/getting_started_5.jpg -------------------------------------------------------------------------------- /Docs/Images/hierarchy_root.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/hierarchy_root.jpg -------------------------------------------------------------------------------- /Docs/Images/import_source_addressable_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/import_source_addressable_1.jpg -------------------------------------------------------------------------------- /Docs/Images/import_source_addressable_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/import_source_addressable_2.jpg -------------------------------------------------------------------------------- /Docs/Images/import_source_addressable_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/import_source_addressable_3.jpg -------------------------------------------------------------------------------- /Docs/Images/import_source_addressable_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/import_source_addressable_3.png -------------------------------------------------------------------------------- /Docs/Images/import_sources_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/import_sources_1.jpg -------------------------------------------------------------------------------- /Docs/Images/import_sources_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/import_sources_2.jpg -------------------------------------------------------------------------------- /Docs/Images/import_sources_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/import_sources_3.jpg -------------------------------------------------------------------------------- /Docs/Images/import_sources_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/import_sources_4.jpg -------------------------------------------------------------------------------- /Docs/Images/import_sources_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/import_sources_5.jpg -------------------------------------------------------------------------------- /Docs/Images/inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/inspector.png -------------------------------------------------------------------------------- /Docs/Images/inspector_configuration_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/inspector_configuration_1.jpg -------------------------------------------------------------------------------- /Docs/Images/inspector_configuration_window.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/inspector_configuration_window.jpg -------------------------------------------------------------------------------- /Docs/Images/instantiate_asset_example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/instantiate_asset_example.jpg -------------------------------------------------------------------------------- /Docs/Images/jint_scripting_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/jint_scripting_1.jpg -------------------------------------------------------------------------------- /Docs/Images/jint_scripting_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/jint_scripting_2.jpg -------------------------------------------------------------------------------- /Docs/Images/jint_scripting_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/jint_scripting_3.jpg -------------------------------------------------------------------------------- /Docs/Images/jint_scripting_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/jint_scripting_4.jpg -------------------------------------------------------------------------------- /Docs/Images/jint_scripting_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/jint_scripting_5.jpg -------------------------------------------------------------------------------- /Docs/Images/jint_scripting_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/jint_scripting_6.jpg -------------------------------------------------------------------------------- /Docs/Images/jint_scripting_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/jint_scripting_7.jpg -------------------------------------------------------------------------------- /Docs/Images/jint_scripting_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/jint_scripting_8.jpg -------------------------------------------------------------------------------- /Docs/Images/jint_scripting_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/jint_scripting_9.jpg -------------------------------------------------------------------------------- /Docs/Images/main_menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/main_menu.jpg -------------------------------------------------------------------------------- /Docs/Images/manage_projects_example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/manage_projects_example.jpg -------------------------------------------------------------------------------- /Docs/Images/manage_projects_example_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/manage_projects_example_2.jpg -------------------------------------------------------------------------------- /Docs/Images/modify_importsource_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/modify_importsource_1.jpg -------------------------------------------------------------------------------- /Docs/Images/modify_importsource_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/modify_importsource_2.jpg -------------------------------------------------------------------------------- /Docs/Images/modify_importsource_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/modify_importsource_3.jpg -------------------------------------------------------------------------------- /Docs/Images/my_component.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/my_component.jpg -------------------------------------------------------------------------------- /Docs/Images/my_import_source_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/my_import_source_1.jpg -------------------------------------------------------------------------------- /Docs/Images/my_import_source_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/my_import_source_2.jpg -------------------------------------------------------------------------------- /Docs/Images/override_layout_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/override_layout_1.jpg -------------------------------------------------------------------------------- /Docs/Images/override_layout_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/override_layout_2.jpg -------------------------------------------------------------------------------- /Docs/Images/override_layout_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/override_layout_3.jpg -------------------------------------------------------------------------------- /Docs/Images/override_scene_params.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/override_scene_params.jpg -------------------------------------------------------------------------------- /Docs/Images/override_theme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/override_theme.jpg -------------------------------------------------------------------------------- /Docs/Images/override_tools.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/override_tools.jpg -------------------------------------------------------------------------------- /Docs/Images/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/overview.png -------------------------------------------------------------------------------- /Docs/Images/persistent_data_path.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/persistent_data_path.jpg -------------------------------------------------------------------------------- /Docs/Images/property_editors.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/property_editors.jpg -------------------------------------------------------------------------------- /Docs/Images/surrogates_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/surrogates_folder.png -------------------------------------------------------------------------------- /Docs/Images/trilib_loader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/trilib_loader.jpg -------------------------------------------------------------------------------- /Docs/Images/ui-scale-after-override.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/ui-scale-after-override.png -------------------------------------------------------------------------------- /Docs/Images/ui-scale-before-override.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/ui-scale-before-override.png -------------------------------------------------------------------------------- /Docs/Images/universal_rp_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/universal_rp_1.jpg -------------------------------------------------------------------------------- /Docs/Images/universal_rp_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/universal_rp_2.jpg -------------------------------------------------------------------------------- /Docs/Images/universal_rp_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/universal_rp_3.jpg -------------------------------------------------------------------------------- /Docs/Images/use_zero_time_scale_in_edit_mode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/use_zero_time_scale_in_edit_mode.jpg -------------------------------------------------------------------------------- /Docs/Images/window.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BattlehubCode/RTE_Docs/29986890e7f9ae7cc3dedee3b61fbcb548040d23/Docs/Images/window.jpg -------------------------------------------------------------------------------- /Misc/MarkDownToHTML.md: -------------------------------------------------------------------------------- 1 | 2 | # Repo 3 | 4 | https://github.com/Battlehub0x/markdown-to-html-github-style 5 | 6 | # Dependencies 7 | 8 | ``` 9 | npm install 10 | ``` 11 | 12 | # Usage 13 | ``` 14 | node convert.js MyPageTitle 15 | ``` 16 | 17 | > Note **Add ** move <style> to header 18 | 19 | 20 | [https://rteditor.battlehub.net/](#https://rteditor.battlehub.net/) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Runtime Editor for Unity 2 | 3 | Welcome to the [**Runtime Editor v.4.5.2**](https://assetstore.unity.com/packages/tools/modeling/runtime-editor-64806) documentation. This asset includes scripts and prefabs designed to help you create scene editors, game level editors, or your own modeling applications. 4 | 5 | [![Asset Store][youtube_icon]](https://assetstore.unity.com/packages/tools/modeling/runtime-editor-64806) 6 | 7 | > **Note** 8 | **Documentation for the previous versions can be found** [**here**](https://rteditor.battlehub.net/v350/). 9 | 10 | > **GPT Assistant can be found** [**here**](https://chatgpt.com/g/g-9Dws8Nfb2-runtime-editor-assistant) 11 | 12 | # Contents 13 | 14 | <!-- TOC start (generated with https://github.com/derlin/bitdowntoc) --> 15 | 16 | - [Introduction](#introduction) 17 | - [Getting Started](#getting-started) 18 | - [Example Scenes](#example-scenes) 19 | - [Compatibility Modes](#compatibility-modes) 20 | + [None](#none) 21 | + [AssetDatabaseOverRTSL](#assetdatabaseoverrtsl) 22 | + [LegacyRTSL](#legacyrtsl) 23 | - [RTSL to AssetDatabase Project Converter](#rtsl-to-assetdatabase-project-converter) 24 | - [Universal Render Pipeline Support](#universal-render-pipeline-support) 25 | - [HDRP Support](#hdrp-support) 26 | - [Common Infrastructure](#common-infrastructure) 27 | * [Overview](#overview) 28 | * [Expose To Editor](#expose-to-editor) 29 | * [IOC](#ioc) 30 | * [Runtime Selection](#runtime-selection) 31 | + [Properties](#properties) 32 | + [Events](#events) 33 | + [Methods](#methods) 34 | * [Runtime Objects](#runtime-objects) 35 | + [Events](#events-1) 36 | + [Methods](#methods-1) 37 | + [Example Usage](#example-usage) 38 | * [Runtime Tools](#runtime-tools) 39 | + [Example Usage](#example-usage-1) 40 | * [Runtime Undo](#runtime-undo) 41 | + [Example Usage](#example-usage-2) 42 | * [Drag And Drop](#drag-and-drop) 43 | + [Example Usage](#example-usage-3) 44 | * [Time Scale](#time-scale) 45 | - [Runtime Editor UI and Window System](#runtime-editor-ui-and-window-system) 46 | * [Overview](#overview-1) 47 | * [RuntimeWindow](#runtimewindow) 48 | + [Key Properties of `RuntimeWindow`](#key-properties-of-runtimewindow) 49 | + [Example Usage](#example-usage-4) 50 | * [Built In Windows](#built-in-windows) 51 | * [Window Manager](#window-manager) 52 | + [Properties and Methods](#properties-and-methods) 53 | - [Properties](#properties-1) 54 | - [Methods](#methods-2) 55 | + [Getting the Window Manager](#getting-the-window-manager) 56 | + [Showing a Message Box](#showing-a-message-box) 57 | + [Showing a Confirmation Dialog](#showing-a-confirmation-dialog) 58 | + [Activating a Window](#activating-a-window) 59 | + [Creating a Window](#creating-a-window) 60 | + [Creating a Dialog Window](#creating-a-dialog-window) 61 | * [Main and Context Menu](#main-and-context-menu) 62 | + [Extending the Main Menu](#extending-the-main-menu) 63 | + [Instance Methods as Menu Commands](#instance-methods-as-menu-commands) 64 | + [MenuDefinition with the sceneName parameter](#menudefinition-with-the-scenename-parameter) 65 | + [Opening a Context Menu with Custom Commands](#opening-a-context-menu-with-custom-commands) 66 | - [Events](#events-2) 67 | - [Methods](#methods-3) 68 | * [Editor Extension](#editor-extension) 69 | + [Methods](#methods-4) 70 | + [Example](#example) 71 | + [Additional Convenience Extension Base Classes](#additional-convenience-extension-base-classes) 72 | - [LayoutExtension](#layoutextension) 73 | * [Methods](#methods-5) 74 | * [Example](#example-1) 75 | - [RuntimeWindowExtension](#runtimewindowextension) 76 | * [Methods](#methods-6) 77 | * [Example](#example-2) 78 | - [SceneComponentExtension](#scenecomponentextension) 79 | * [Methods](#methods-7) 80 | * [Example](#example-3) 81 | * [Window](#window) 82 | - [View-ViewModel-ViewBinding Architecture](#view-viewmodel-viewbinding-architecture) 83 | + [UnityWeld](#unityweld) 84 | + [View](#view) 85 | - [Key Properties](#key-properties) 86 | - [Key Events](#key-events) 87 | - [Key Methods](#key-methods) 88 | - [Utility Methods](#utility-methods) 89 | - [Example Usage](#example-usage-5) 90 | + [ViewModel](#viewmodel) 91 | - [Key Properties](#key-properties-1) 92 | - [Key Methods](#key-methods-1) 93 | - [Example Usage](#example-usage-6) 94 | + [HierarchicalDataViewModel ](#hierarchicaldataviewmodel) 95 | - [Key Properties](#key-properties-2) 96 | - [Key Events](#key-events-1) 97 | - [Key Methods](#key-methods-2) 98 | - [Context Menu Handling](#context-menu-handling) 99 | - [IHierarchicalData Implementation](#ihierarchicaldata-implementation) 100 | - [Bound UnityEvent Handlers](#bound-unityevent-handlers) 101 | - [Example Usage](#example-usage-7) 102 | * [Custom windows ](#custom-windows) 103 | * [Extending Existing Windows](#extending-existing-windows) 104 | + [1. Override the Window Prefab](#1-override-the-window-prefab) 105 | + [2. Override Programmatically Using Layout Extension](#2-override-programmatically-using-layout-extension) 106 | + [3. Override ViewModel or View Using RuntimeWindowExtension and ReplaceWith Method](#3-override-viewmodel-or-view-using-runtimewindowextension-and-replacewith-method) 107 | * [Overriding the Default Layout](#overriding-the-default-layout) 108 | + [Example](#example-4) 109 | + [LayoutInfo Methods](#layoutinfo-methods) 110 | - [Vertical Split](#vertical-split) 111 | - [Horizontal Split](#horizontal-split) 112 | - [Tab Group](#tab-group) 113 | - [Vertical Split Example](#vertical-split-example) 114 | - [Horizontal Split Example](#horizontal-split-example) 115 | - [Tab Group Example](#tab-group-example) 116 | * [Overriding Scene Parameters](#overriding-scene-parameters) 117 | + [Example](#example-5) 118 | + [Explanation](#explanation) 119 | * [Overriding Tools Panel](#overriding-tools-panel) 120 | * [Setting ui scale](#setting-ui-scale) 121 | * [Overriding UI Scale](#overriding-ui-scale) 122 | + [Example Script](#example-script) 123 | - [Before UI Scale Override](#before-ui-scale-override) 124 | - [After UI Scale Override](#after-ui-scale-override) 125 | * [Overriding the Theme](#overriding-the-theme) 126 | + [Example Script](#example-script-1) 127 | * [Inspector View](#inspector-view) 128 | + [Inspector Editors](#inspector-editors) 129 | + [Property Editors](#property-editors) 130 | * [Inspector Configuration](#inspector-configuration) 131 | + [Register Editors Programatically](#register-editors-programatically) 132 | + [Steps to Register Property Editors Programmatically](#steps-to-register-property-editors-programmatically) 133 | + [Component Properties Visibility](#component-properties-visibility) 134 | + [Custom Type Properties Visibility](#custom-type-properties-visibility) 135 | + [Customizing Component Editor Header](#customizing-component-editor-header) 136 | + [HeaderDescriptor Structure](#headerdescriptor-structure) 137 | * [Localization](#localization) 138 | + [Built-in String Resources](#built-in-string-resources) 139 | - [UI Controls](#ui-controls) 140 | * [Dock Panel Control](#dock-panel-control) 141 | * [Tree View Control](#tree-view-control) 142 | * [Menu Control](#menu-control) 143 | - [Runtime Transform Handles](#runtime-transform-handles) 144 | * [Runtime Gizmos](#runtime-gizmos) 145 | - [Animation Editor ](#animation-editor) 146 | - [Runtime Editor Extensions](#runtime-editor-extensions) 147 | - [Runtime Scripting](#runtime-scripting) 148 | * [Getting Started with Runtime Scripting](#getting-started-with-runtime-scripting) 149 | * [Jint Script](#jint-script) 150 | * [Declaring Variables](#declaring-variables) 151 | * [Using UnityEngine API](#using-unityengine-api) 152 | * [JintComponent.Add, JintComponent.Get](#jintcomponentadd-jintcomponentget) 153 | * [Create Jint Script Programmatically](#create-jint-script-programmatically) 154 | * [More Runtime Scripting Samples](#more-runtime-scripting-samples) 155 | - [Asset Database](#asset-database) 156 | * [IRuntimeEditor and IAssetDatabaseModel Interfaces](#iruntimeeditor-and-iassetdatabasemodel-interfaces) 157 | + [IRuntimeEditor Interface](#iruntimeeditor-interface) 158 | + [IAssetDatabaseModel Interface](#iassetdatabasemodel-interface) 159 | + [Key Methods and Properties](#key-methods-and-properties) 160 | - [IRuntimeEditor](#iruntimeeditor) 161 | - [IAssetDatabaseModel](#iassetdatabasemodel) 162 | * [IAssetDatabaseModel vs IRuntimeEditor Interface](#iassetdatabasemodel-vs-iruntimeeditor-interface) 163 | * [Projects Root Folder Path](#projects-root-folder-path) 164 | * [Manage Projects](#manage-projects) 165 | * [Import Export Project](#import-export-project) 166 | * [Create Folder](#create-folder) 167 | * [Set Current Folder](#set-current-folder) 168 | * [Runtime Scene](#runtime-scene) 169 | * [Create New, Save, Load scene](#create-new-save-load-scene) 170 | * [Create, Save, Load, Delete Assets and Folders](#create-save-load-delete-assets-and-folders) 171 | * [Instantiate asset](#instantiate-asset) 172 | * [Duplicate Asset](#duplicate-asset) 173 | * [Move Asset](#move-asset) 174 | * [Import Export Assets](#import-export-assets) 175 | * [External Asset Loaders](#external-asset-loaders) 176 | + [TriLibLoader Example](#trilibloader-example) 177 | - [Implement IExternalAssetLoaderModel](#implement-iexternalassetloadermodel) 178 | - [Register TriLibLoader](#register-trilibloader) 179 | - [Import FBX as External Asset](#import-fbx-as-external-asset) 180 | * [Import Sources](#import-sources) 181 | + [Resources Import Source](#resources-import-source) 182 | + [Addressables Import Source](#addressables-import-source) 183 | + [Custom Import Source](#custom-import-source) 184 | * [File Importers](#file-importers) 185 | + [Custom File Importer](#custom-file-importer) 186 | * [Web Storage Sample](#web-storage-sample) 187 | * [Serializer Extensions](#serializer-extensions) 188 | * [Enumerators](#enumerators) 189 | * [Dynamic Surrogates (Preview)](#dynamic-surrogates-preview) 190 | * [Build All](#build-all) 191 | * [Support](#support) 192 | 193 | <!-- TOC end --> 194 | 195 | # Introduction 196 | The Runtime Editor consists of several major parts, effectively decoupled using MVVM and dependency injection mechanisms: 197 | - **Common Infrastructure APIs**: Includes Selection, Tools, IOC, and Drag & Drop. 198 | - **UI Controls**: DockPanel, TreeView, Main Menu, and Context Menu. 199 | - **WindowManager**: Provides built-in windows and dialogs like Inspector, Scene, Hierarchy, Project, and Console. 200 | - **Transform Handles & Gizmos**: Facilitates scene controls and navigation. 201 | - **Runtime Asset Database**: Manages assets, scenes, and prefabs. 202 | - **Extensions**: Includes Terrain Editor, Animation Editor, ProBuilder Editor, and Runtime Scripting. 203 | 204 | # Getting Started 205 | 1. Create a new scene. 206 | 2. Click **Tools -> Runtime Editor -> Create RTEditor**. 207 | ![Create RuntimeEditor][getting_started_1] 208 | 3. Optionally, click **Tools -> Runtime Editor -> Create RTExtensions**. 209 | 4. Click "Yes" when prompted to add the Game View Camera script. 210 | ![Create Game View Camera Script][getting_started_2] 211 | 5. Create several Game Objects and add the [Expose To Editor](#expose-to-editor) component. 212 | ![Add Expose To Editor][getting_started_3] 213 | 6. Move these game objects to the Scene game object children. 214 | ![Scene Children][getting_started_4] 215 | 7. Press "Play". 216 | ![Getting Started Result][getting_started_5] 217 | 218 | # Example Scenes 219 | There are various example scenes available. To access them: 220 | 221 | 1. Unpack "Assets/Battlehub/**2 RTEditor Demo.unitypackage**". 222 | ![Unpack 2 RTEditor Demo][example_scenes_1] 223 | 2. Click **Tools -> Runtime Editor -> Show me examples**. 224 | ![Show Me Examples][example_scenes_2] 225 | 226 | > **Note** 227 | If you are using Universal or HDRP, when you open the demo scene, also follow the steps in [Universal Render Pipeline Support](#universal-render-pipeline-support) or [HDRP Support](#hdrp-support). 228 | 229 | # Compatibility Modes 230 | In Runtime Editor 4.x.x, the [Runtime Asset Database](https://github.com/Battlehub0x/RuntimeAssetDatabase) has replaced the [Runtime Save Load](https://rteditor.battlehub.net/v350/manual/save-load.html) subsystem, which is now considered legacy. By default, Runtime Editor cannot open projects created using RTSL. However, there are two compatibility modes, **AssetDatabaseOverRTSL** and **LegacyRTSL**, 231 | which allow you to open and work with projects created before RTE 4.x.x. 232 | 233 | > **Note** 234 | Before selecting compatibility mode, extract **Assets/Battlehub/LegacyRTSL.unitypackage** 235 | 236 | To select a compatibility mode, use the **Compatibility Mode** dropdown in the Runtime Editor component editor. 237 | ![Compatibility Mode][compatibility_mode] 238 | 239 | ### None 240 | In this mode, users will not be able to open legacy RTSL projects. The Project window is replaced with the AssetDatabase window, the Save Scene Dialog and Save Asset Dialog are replaced with the AssetDatabaseSave dialog, and the Select Object Dialog is replaced with the AssetDatabaseSelect dialog. Additional compatibility prefabs like RTSLDeps and AssetDatabaseOverRTSL, along with corresponding compatibility scripts, are not created. 241 | 242 | ### AssetDatabaseOverRTSL 243 | In this mode, users can open both new Asset Database projects and legacy RTSL projects. The Project window is replaced with the AssetDatabase window, the Save Scene Dialog and Save Asset Dialog are replaced with the AssetDatabaseSave dialog, and the Select Object Dialog is replaced with the AssetDatabaseSelect dialog. Old IProjectAsync APIs are available, but corresponding functions must be called using the IRuntimeEditor or IAssetDatabaseModel interface, which is implemented by the AssetDatabaseOverRTSL class. 244 | 245 | ### LegacyRTSL 246 | In this mode, users can open only legacy RTSL projects. All legacy windows and APIs, such as IProjectAsync, are available. While it is possible to use some new methods of the IRuntimeEditor interface and IAssetDatabase interface, some of them might throw exceptions. This mode is not recommended. 247 | 248 | # RTSL to AssetDatabase Project Converter 249 | 250 | An example of a project converter from the old RTSL format to the new AssetDatabase format can be found in [Assets\Battlehub\RTEditorDemo\Examples\RTSL to AssetDatabase Project Converter.unity](#example-scenes). 251 | 252 | > **Note** 253 | To access this example, first extract **Assets/Battlehub/LegacyRTSL.unitypackage** 254 | 255 | The converter is implemented in the `RTSLProjectConverter` class. It takes the RTSL project path and the new AssetDatabase project path as inputs. The converter performs the following steps: 256 | 257 | 1. Copies the folder structure. 258 | 2. Converts assets like materials, meshes, etc. 259 | 3. Converts prefabs. 260 | 4. Converts scenes. 261 | 262 | # Universal Render Pipeline Support 263 | 1. Unpack "Assets/Battlehub/**1 UniversalRP Support.unitypackage**". 264 | ![Unpack 1 Universal RP][universal_rp_1] 265 | 2. Click "Install/Upgrade" dependencies to install the required Universal Render Pipeline package. 266 | 3. Click **Tools -> Runtime Editor -> Use URP RenderPipelineAsset**. 267 | 4. Click **Tools -> Runtime Editor -> Add URP support for RTEditor**. 268 | 5. If you are using Runtime Editor Extensions (RTExtensions prefab), click **Tools -> Runtime Editor -> Add URP support for RTExtensions**. 269 | ![Add Required Prefabs][universal_rp_2] 270 | 6. In Project Settings / Player, switch the color space to **Linear**. 271 | ![Linear Color Space][universal_rp_3] 272 | 273 | > **Note** 274 | > If the scene window will always be docked, set `RenderPipelineInfo.UserForegroundLayerForUI = false`. See the [MinimalLayoutExample script in Scene1 and Scene2](#example-scenes) for details. 275 | 276 | # HDRP Support 277 | The procedure is the same as for [URP Support](#universal-render-pipeline-support), except you need to unpack Assets/Battlehub/**6 HDRP Support.unitypackage** and use the corresponding **HDRP** menu items. 278 | 279 | # Common Infrastructure 280 | ## Overview 281 | Common infrastructure classes and interfaces form the core API of the Runtime Editor. This includes selection, object lifecycle, tools, undo-redo, and drag-and-drop APIs. 282 | 283 | ## Expose To Editor 284 | Add the Assets/Battlehub/RTEditor/Runtime/RTCommon/**ExposeToEditor** component to any Game Object you want to make available for selection and editing. 285 | 286 | ![Expose To Editor][expose_to_editor] 287 | 288 | 1. **Selection Events**: 289 | - **Selected (ExposeToEditor)** 290 | - **Unselected (ExposeToEditor)** 291 | 292 | 2. **Bounds Configuration**: 293 | - **Bounds Object**: Specifies the GameObject to which the collider will be added (if AddColliders == false) 294 | - **Bounds Type**: Determines the type of bounds to be used. 295 | - **Custom Bounds**: If Bounds Type is set to Custom, it allows for specifying custom bounds with center and extents. 296 | 297 | 3. **Capabilities**: 298 | - **Can Transform**, **Can Inspect**, **Can Duplicate**, **Can Delete**, **Can Rename**, **Can Create Prefab**, **Show Selection Gizmo**: These options are self-explanatory. 299 | 300 | 4. **Collider Management**: 301 | - **Add Colliders**: You can unselect the Add Colliders option if you want to specify the collider to be used for selection yourself. If this option is selected, a MeshCollider will be added automatically.. 302 | 303 | ## IOC 304 | Assets/Battlehub/RTEditor/Runtime/RTCommon/Utils/**IOC.cs** is a simple IoC container implementation. 305 | 306 | **Methods**: 307 | 308 | - `static T Resolve<T>()`: Resolve a dependency of type 'T'. 309 | - `static void Register<T>(Func<T> func)`: Register a construction function. 310 | - `static void Register<T>(T instance)`: Register an instance. 311 | - `static void Unregister<T>(Func<T> func)`: Unregister a construction function. 312 | - `static void Unregister<T>(T instance)`: Unregister an instance. 313 | 314 | **Example 1**: 315 | 316 | ```csharp 317 | using UnityEngine; 318 | using Battlehub.RTCommon; 319 | 320 | public interface IDependency { } 321 | 322 | public class Dependency : MonoBehaviour, IDependency 323 | { 324 | void Awake() 325 | { 326 | IOC.Register<IDependency>(this); 327 | } 328 | 329 | void OnDestroy() 330 | { 331 | IOC.Unregister<IDependency>(this); 332 | } 333 | } 334 | 335 | public class User : MonoBehaviour 336 | { 337 | void Start() 338 | { 339 | IDependency dependency = IOC.Resolve<IDependency>(); 340 | } 341 | } 342 | ``` 343 | 344 | **Example 2**: 345 | 346 | ```csharp 347 | using UnityEngine; 348 | using Battlehub.RTCommon; 349 | 350 | [DefaultExecutionOrder(-1)] 351 | public class Registrar : MonoBehaviour 352 | { 353 | void Awake() 354 | { 355 | IOC.Register<IDependency>(() => 356 | { 357 | GameObject go = new GameObject(); 358 | return go.AddComponent<Dependency>(); 359 | }); 360 | } 361 | 362 | private void OnDestroy() 363 | { 364 | IOC.Unregister<IDependency>(); 365 | } 366 | } 367 | 368 | public interface IDependency { } 369 | 370 | public class Dependency : MonoBehaviour, IDependency 371 | { 372 | } 373 | 374 | public class User : MonoBehaviour 375 | { 376 | void Awake() 377 | { 378 | IDependency dependency = IOC.Resolve<IDependency>(); 379 | } 380 | } 381 | ``` 382 | 383 | ## Runtime Selection 384 | 385 | The `IRuntimeSelection` interface is a key part of the Runtime Editor's API, providing functionality to manage and interact with the selection of objects within the editor. Here is a detailed description of its properties and methods: 386 | 387 | ### Properties 388 | - **Enabled**: Gets or sets a value indicating whether the selection functionality is enabled. 389 | - **EnableUndo**: Gets or sets a value indicating whether undo functionality is enabled for selection changes. 390 | - **activeGameObject**: Gets or sets the currently active selected GameObject. 391 | - **activeObject**: Gets or sets the currently active selected object. 392 | - **objects**: Gets or sets the array of currently selected objects. 393 | - **gameObjects**: Gets an array of the currently selected GameObjects. 394 | - **activeTransform**: Gets the Transform of the currently active selected GameObject. 395 | - **Length**: Gets the number of currently selected objects. 396 | 397 | ### Events 398 | - **SelectionChanged**: An event that is triggered when the selection changes. Subscribers to this event can perform actions in response to selection changes. 399 | 400 | ### Methods 401 | - **IsSelected(Object obj)**: Determines whether a given object is currently selected. 402 | - **Select(Object activeObject, Object[] selection)**: Selects the specified objects, with the specified object set as the active object. 403 | 404 | This interface allows for robust selection management within the Runtime Editor, supporting multiple selection states, undo functionality, and event-driven responses to selection changes. 405 | 406 | ```csharp 407 | using UnityEngine; 408 | using Battlehub.RTCommon; 409 | 410 | public class RuntimeSelectionExample : MonoBehaviour 411 | { 412 | IRuntimeSelection m_selection; 413 | 414 | void Start() 415 | { 416 | var editor = IOC.Resolve<IRTE>(); 417 | m_selection = editor.Selection; 418 | 419 | // Subscribe to the selection changed event 420 | m_selection.SelectionChanged += OnSelectionChanged; 421 | 422 | // Create a sample game object 423 | var go = GameObject.CreatePrimitive(PrimitiveType.Capsule); 424 | go.AddComponent<ExposeToEditor>(); 425 | 426 | // Add object to the scene 427 | editor.AddGameObjectToHierarchy(go); 428 | 429 | // Select the game object 430 | m_selection.activeObject = go; 431 | } 432 | 433 | void OnDestroy() 434 | { 435 | if (m_selection != null) 436 | { 437 | m_selection.SelectionChanged -= OnSelectionChanged; 438 | } 439 | } 440 | 441 | void OnSelectionChanged(Object[] unselectedObjects) 442 | { 443 | if (unselectedObjects != null) 444 | { 445 | for (int i = 0; i < unselectedObjects.Length; ++i) 446 | { 447 | Object unselected = unselectedObjects[i]; 448 | Debug.Log("Unselected: " + unselected.name); 449 | } 450 | } 451 | 452 | if (m_selection.objects != null) 453 | { 454 | for (int i = 0; i < m_selection.objects.Length; ++i) 455 | { 456 | Object selected = m_selection.objects[i]; 457 | Debug.Log("Selected: " + selected.name); 458 | } 459 | } 460 | } 461 | } 462 | ``` 463 | 464 | > **Note** <br/> 465 | For more examples, see **Scene20 - Selection** in the [Example Scenes section](#example-scenes). 466 | 467 | 468 | ## Runtime Objects 469 | The `IRuntimeObjects` interface is designed to provide events and methods for managing and responding to changes in runtime objects within the Runtime Editor environment. 470 | This interface enables the monitoring of various object lifecycle events and component-related changes. 471 | 472 | ### Events 473 | - **Awaked**: Triggered when an object awakens (i.e., when its Awake method is called). 474 | - **Started**: Triggered when an object starts (i.e., when its Start method is called). 475 | - **Enabled**: Triggered when an object is enabled. 476 | - **Disabled**: Triggered when an object is disabled. 477 | - **Destroying**: Triggered when an object is about to be destroyed. 478 | - **Destroyed**: Triggered when an object has been destroyed. 479 | - **MarkAsDestroyedChanging**: Triggered when an object's "mark as destroyed" status is about to change. 480 | - **MarkAsDestroyedChanged**: Triggered when an object's "mark as destroyed" status has changed. 481 | - **TransformChanged**: Triggered when an object's transform has changed. 482 | - **NameChanged**: Triggered when an object's name has changed. 483 | - **ParentChanged**: Triggered when an object's parent has changed. 484 | - **ComponentAdded**: Triggered when a component is added to an object. 485 | - **ComponentDestroyed**: Triggered when a component is removed from an object. 486 | - **ReloadComponentEditor**: Triggered when a component editor needs to be reloaded, with a boolean parameter indicating whether the component was just added or is being reloaded. 487 | 488 | ### Methods 489 | - **IEnumerable<ExposeToEditor> Get(bool rootsOnly, bool useCache = true)**: Retrieves a collection of objects exposed to the editor. The `rootsOnly` parameter specifies whether to include only root objects, and `useCache` indicates whether to use cache instead of Unity Engine API calls to find objects. 490 | 491 | ### Example Usage 492 | 493 | ```csharp 494 | using UnityEngine; 495 | using Battlehub.RTCommon; 496 | 497 | public class ListenAwakeEventExample : MonoBehaviour 498 | { 499 | IRuntimeObjects m_object; 500 | 501 | void Start() 502 | { 503 | m_object = IOC.Resolve<IRTE>().Object; 504 | m_object.Awaked += OnObjectAwaked; 505 | 506 | GameObject go = new GameObject(); 507 | go.AddComponent<ExposeToEditor>(); 508 | } 509 | 510 | void OnDestroy() 511 | { 512 | if (m_object != null) 513 | { 514 | m_object.Awaked -= OnObjectAwaked; 515 | } 516 | } 517 | 518 | void OnObjectAwaked(ExposeToEditor obj) 519 | { 520 | Debug.Log("Awake: " + obj); 521 | } 522 | } 523 | ``` 524 | 525 | > **Note** <br/> 526 | For more examples, see **Scene24 - Object Lifecycle events** in the [Example Scenes section](#example-scenes). 527 | 528 | ## Runtime Tools 529 | 530 | The RuntimeTools class allows you to track and manipulate the current tool, locking state, pivot modes, and other aspects of the editor's toolset. 531 | 532 | **Properties**: 533 | 534 | - **LockObject LockAxes**: Holds the aggregated locking state of selected objects. 535 | - **UnityObject ActiveTool**: Reference to the active transform handle or gizmo. Active means that the user is currently interacting with it. 536 | - **RuntimeTool Current**: The tool that is currently enabled for the Scene View: 537 | - None 538 | - Move 539 | - Rotate 540 | - Scale 541 | - View 542 | - Rect 543 | - Custom 544 | - **RuntimePivotMode PivotMode**: Indicates whether we are in Center or Pivot mode. 545 | - **RuntimePivotRotation PivotRotation**: Specifies the rotation of the tool handle: 546 | - Global 547 | - Local 548 | 549 | **Events**: 550 | 551 | - **event RuntimeToolsEvent ToolChanged**: Raised when the current tool changes. 552 | - **event RuntimeToolsEvent PivotRotationChanged**: Raised when the pivot rotation changes. 553 | - **event RuntimeToolsEvent PivotModeChanged**: Raised when the pivot mode changes. 554 | - **event RuntimeToolsEvent LockAxesChanged**: Raised when the lock axes change. 555 | 556 | **Additional Properties and Events**: 557 | 558 | - **IsViewing**: Indicates whether the editor is in viewing mode. Changing this property raises the `IsViewingChanged` event. 559 | - **AutoFocus**: Indicates whether auto-focus is enabled. Changing this property raises the `AutoFocusChanged` event. 560 | - **UnitSnapping**: Indicates whether unit snapping is enabled. Changing this property raises the `UnitSnappingChanged` event. 561 | - **IsSnapping**: Indicates whether snapping is enabled. Changing this property raises the `IsSnappingChanged` event. 562 | - **SnappingMode**: Specifies the snapping mode. Changing this property raises the `SnappingModeChanged` event. 563 | - **CustomPivotPosition**: Specifies a custom pivot position. 564 | - **SelectionMode**: Specifies the selection mode. Changing this property raises the `SelectionModeChanged` event. 565 | - **IsBoxSelectionEnabled**: Indicates whether box selection is enabled. Changing this property raises the `IsBoxSelectionEnabledChanged` event. 566 | 567 | ### Example Usage 568 | 569 | **Changing the Current Tool**: 570 | 571 | ```csharp 572 | using UnityEngine; 573 | using Battlehub.RTCommon; 574 | 575 | public class SwitchToolBehaviour : MonoBehaviour 576 | { 577 | void Start() 578 | { 579 | IRTE editor = IOC.Resolve<IRTE>(); 580 | editor.Tools.Current = RuntimeTool.Rotate; 581 | } 582 | } 583 | ``` 584 | 585 | **Locking Axes for All Selected Objects**: 586 | 587 | ```csharp 588 | using UnityEngine; 589 | using Battlehub.RTCommon; 590 | 591 | public class LockAxesForAllObjects : MonoBehaviour 592 | { 593 | IRTE m_editor; 594 | void Start() 595 | { 596 | m_editor = IOC.Resolve<IRTE>(); 597 | m_editor.Selection.SelectionChanged += OnSelectionChanged; 598 | m_editor.Tools.ToolChanged += OnToolChanged; 599 | } 600 | 601 | void OnDestroy() 602 | { 603 | if(m_editor != null) 604 | { 605 | m_editor.Selection.SelectionChanged -= OnSelectionChanged; 606 | m_editor.Tools.ToolChanged -= OnToolChanged; 607 | } 608 | } 609 | 610 | void OnToolChanged() 611 | { 612 | Lock(); 613 | } 614 | 615 | void OnSelectionChanged(Object[] unselectedObjects) 616 | { 617 | Lock(); 618 | } 619 | 620 | static void Lock() 621 | { 622 | IRTE editor = IOC.Resolve<IRTE>(); 623 | editor.Tools.LockAxes = new LockObject 624 | { 625 | PositionY = true, 626 | RotationX = true, 627 | RotationZ = true 628 | }; 629 | } 630 | } 631 | ``` 632 | 633 | > **Note** <br/> 634 | For more examples, see **Scene21 - Tools** in the [Example Scenes section](#example-scenes). 635 | 636 | 637 | ## Runtime Undo 638 | 639 | The `IRuntimeUndo` interface is used to record changes, maintain the undo/redo stack, and perform undo and redo operations. 640 | 641 | **Properties**: 642 | 643 | - **bool Enabled { get; set; }**: Indicates whether undo and redo operations are enabled. 644 | - **bool CanUndo { get; }**: Indicates whether an undo operation can be performed. 645 | - **bool CanRedo { get; }**: Indicates whether a redo operation can be performed. 646 | - **bool IsRecording { get; }**: Indicates whether multiple changes are being recorded. 647 | 648 | **Methods**: 649 | 650 | - **void BeginRecord()**: Begins recording multiple changes. 651 | - **void EndRecord()**: Ends recording multiple changes. 652 | - **void GroupRecords(int count = -1)**: Groups the specified number of records into a single undo/redo operation. 653 | - **Record CreateRecord(UndoRedoCallback redoCallback, UndoRedoCallback undoCallback, PurgeCallback purgeCallback = null, EraseReferenceCallback eraseCallback = null)**: Creates a generic record. 654 | - **Record CreateRecord(object target, object newState, object oldState, UndoRedoCallback redoCallback, UndoRedoCallback undoCallback, PurgeCallback purgeCallback = null, EraseReferenceCallback eraseCallback = null)**: Creates a record with a specific target, new state, and old state. 655 | - **void RegisterCreatedObjects(ExposeToEditor[] createdObjects, Action afterRedo = null, Action afterUndo = null)**: Registers created objects. 656 | - **void DestroyObjects(ExposeToEditor[] destroyedObjects, Action afterRedo = null, Action afterUndo = null)**: Registers destroy objects operation. 657 | - **void BeginRecordValue(object target, MemberInfo memberInfo)**: Begins recording a value change. 658 | - **void BeginRecordValue(object target, object accessor, MemberInfo memberInfo)**: Begins recording a value change with accessor. 659 | - **void EndRecordValue(object target, MemberInfo memberInfo, Action afterRedo = null, Action afterUndo = null)**: Ends recording a value change. 660 | - **void EndRecordValue(object target, object accessor, MemberInfo memberInfo, Action<object, object> targetErased = null, Action afterRedo = null, Action afterUndo = null)**: Ends recording a value change with accessor. 661 | - **void BeginRecordTransform(Transform target, Action<Transform> afterUndo = null)**: Begins recording a transform change. 662 | - **void EndRecordTransform(Transform target, Action<Transform> afterRedo = null)**: Ends recording a transform change. 663 | - **void BeginRecordTransform(Transform target, Transform parent, int siblingIndex = -1, Action<Transform> afterUndo = null)**: Begins recording a transform change with parent and sibling index. 664 | - **void EndRecordTransform(Transform target, Transform parent, int siblingIndex = -1, Action<Transform> afterRedo = null)**: Ends recording a transform change with parent and sibling index. 665 | - **void AddComponent(ExposeToEditor obj, Type type)**: Adds a component and pushes the corresponding record to the stack. 666 | - **void AddComponentWithRequirements(ExposeToEditor obj, Type type)**: Adds a component along with its required components. 667 | - **void DestroyComponent(Component destroy, MemberInfo[] memberInfo)**: Destroys a component and pushes the corresponding record to the stack. 668 | - **void Redo()**: Performs a redo operation. 669 | - **void Undo()**: Performs an undo operation. 670 | - **void Purge()**: Purges all records. All “marked as destroyed” objects will be destroyed. 671 | - **void Erase(object oldRef, object newRef = null, bool ignoreLock = false)**: Replaces oldRef with newRef for all records in the stack. 672 | - **void Store()**: Creates a new stack and stores the current undo and redo stack. 673 | - **void Restore()**: Restores a previously stored stack. 674 | 675 | **Events**: 676 | 677 | - **event RuntimeUndoEventHandler BeforeUndo**: Raised before an undo operation. 678 | - **event RuntimeUndoEventHandler UndoCompleted**: Raised after an undo operation. 679 | - **event RuntimeUndoEventHandler BeforeRedo**: Raised before a redo operation. 680 | - **event RuntimeUndoEventHandler RedoCompleted**: Raised after a redo operation. 681 | - **event RuntimeUndoEventHandler StateChanged**: Raised whenever one of the following operations is performed: 682 | - Store 683 | - Restore 684 | - Purge 685 | 686 | ### Example Usage 687 | 688 | **Recording a Value and then Undoing Changes**: 689 | 690 | ```csharp 691 | using UnityEngine; 692 | using System.Reflection; 693 | using Battlehub.RTCommon; 694 | using Battlehub.Utils; 695 | 696 | public class RecordValueThenUndoChanges : MonoBehaviour 697 | { 698 | IRuntimeUndo m_undo; 699 | 700 | [SerializeField] 701 | int m_value = 1; 702 | 703 | void Start() 704 | { 705 | m_undo = IOC.Resolve<IRTE>().Undo; 706 | m_undo.UndoCompleted += OnUndoCompleted; 707 | 708 | var valueInfo = Strong.MemberInfo((RecordValueThenUndoChanges x) => x.m_value); 709 | 710 | m_undo.BeginRecordValue(this, valueInfo); 711 | m_value = 2; 712 | m_undo.EndRecordValue(this, valueInfo); 713 | 714 | m_undo.Undo(); 715 | } 716 | 717 | void OnDestroy() 718 | { 719 | if (m_undo != null) 720 | { 721 | m_undo.UndoCompleted -= OnUndoCompleted; 722 | } 723 | } 724 | 725 | void OnUndoCompleted() 726 | { 727 | Debug.Log(m_value); // 1 728 | } 729 | } 730 | ``` 731 | > **Note** <br/> 732 | For more examples, see **Scene25 - Undo & Redo** in the [Example Scenes section](#example-scenes). 733 | 734 | ## Drag And Drop 735 | The `IDragDrop` interface is used as a common interface for all drag-and-drop operations. 736 | 737 | **Properties**: 738 | 739 | - **object[] DragObjects { get; }**: Objects being dragged. 740 | - **object Source { get; }**: Drag-and-drop operation source object. 741 | - **bool InProgress { get; }**: Indicates if a drag-and-drop operation is in progress. 742 | 743 | **Methods**: 744 | 745 | - **void Reset()**: Cancels the current drag-and-drop operation. 746 | - **void SetCursor(KnownCursor cursorType)**: Sets the cursor type. 747 | - KnownCursor.DropNotAllowed 748 | - KnownCursor.DropAllowed 749 | - **void RaiseBeginDrag(object source, object[] dragItems, PointerEventData pointerEventData)**: Begins a drag-and-drop operation. 750 | - **void RaiseDrag(PointerEventData eventData)**: Handles the drag operation. 751 | - **void RaiseDrop(PointerEventData pointerEventData)**: Ends the drag-and-drop operation. 752 | 753 | **Events**: 754 | 755 | - **event DragDropEventHander BeginDrag**: Raised by the `RaiseBeginDrag` method. 756 | - **event DragDropEventHander Drag**: Raised by the `RaiseDrag` method. 757 | - **event DragDropEventHander Drop**: Raised by the `RaiseDrop` method. 758 | 759 | ### Example Usage 760 | 761 | In this example, we will handle a drag-and-drop operation into the console window (Add this script to the scene and try to drag and drop something from the Hierarchy or Project to the Console) 762 | 763 | ```csharp 764 | using Battlehub.RTCommon; 765 | using UnityEngine; 766 | using UnityEngine.EventSystems; 767 | 768 | public class ConsoleDragDropHandler : MonoBehaviour 769 | { 770 | IDragDrop m_dragDrop; 771 | RuntimeWindow m_target; 772 | 773 | void Start() 774 | { 775 | m_target = IOC.Resolve<IRTE>().GetWindow(RuntimeWindowType.Console); 776 | m_dragDrop = IOC.Resolve<IRTE>().DragDrop; 777 | m_dragDrop.Drop += OnDrop; 778 | } 779 | 780 | void OnDestroy() 781 | { 782 | if(m_dragDrop != null) 783 | { 784 | m_dragDrop.Drop -= OnDrop; 785 | } 786 | } 787 | 788 | void OnDrop(PointerEventData pointerEventData) 789 | { 790 | if(m_target != null && m_target.IsPointerOver) 791 | { 792 | Debug.Log(m_dragDrop.DragObjects[0]); 793 | } 794 | } 795 | } 796 | ``` 797 | 798 | > **Note** <br/> 799 | For more examples, see **Scene11 - Handling Drag and Drop** in the [Example Scenes section](#example-scenes). 800 | 801 | 802 | ## Time Scale 803 | 804 | Sometimes, it's useful to "disable" physics and prevent scripts from updating, effectively pausing your scene. 805 | 806 | To achieve this, `Time.timeScale` is used (see: [Unity documentation](https://docs.unity3d.com/ScriptReference/Time-timeScale.html)). 807 | 808 | To enable a mode where `Time.timeScale` is set to 0 in edit mode and 1 in play mode, set `UseZeroTimeScaleInEditMode` to true (default value is false). 809 | 810 | ![Use Zero TimeScale in Edit Mode][use_zero_time_scale_in_edit_mode] 811 | 812 | # Runtime Editor UI and Window System 813 | ## Overview 814 | 815 | The runtime editor UI is built using Unity's UGUI and includes three complex controls: [Dock Panel](#dock-panel-control), [Tree View](#tree-view-control), and [Menu](#menu-control). 816 | 817 | 818 | The Runtime Editor Windows system is primarily managed using the `IWindowManager` interface. It allows for the creation of complex windows, such as inspectors or scenes, and simple dialogs, like messages or confirmation boxes. A key distinction is that dialogs cannot be docked and do not deactivate other windows, whereas windows can. 819 | All windows and dialogs require a `RuntimeWindow` component to function correctly. 820 | 821 | ## RuntimeWindow 822 | The RuntimeWindow class provides essential properties and methods to manage window behavior, activation, and interaction within the Runtime Editor 823 | 824 | ### Key Properties of `RuntimeWindow` 825 | 826 | 1. **CanActivate**: 827 | - **Type**: `bool` 828 | - **Description**: Indicates whether the window can be activated. 829 | - **Default**: `true` 830 | 831 | 2. **ActivateOnAnyKey**: 832 | - **Type**: `bool` 833 | - **Description**: Specifies if the window should activate on any key press. 834 | - **Default**: `false` 835 | 836 | 3. **Camera**: 837 | - **Type**: `Camera` 838 | - **Description**: The camera associated with the window. 839 | - **Default**: `null` 840 | - **Behavior**: Throws `NotSupportedException` if set. 841 | 842 | 4. **Pointer**: 843 | - **Type**: `Pointer` 844 | - **Description**: Manages pointer events for the window. 845 | 846 | 5. **IOCContainer**: 847 | - **Type**: `IOCContainer` 848 | - **Description**: Inversion of Control container for dependency management within the window. 849 | 850 | 6. **WindowType**: 851 | - **Type**: `RuntimeWindowType` 852 | - **Description**: Specifies the type of the window (e.g., Scene, Game). 853 | - **Default**: `RuntimeWindowType.Scene` 854 | 855 | 7. **Index**: 856 | - **Type**: `int` 857 | - **Description**: Index used to identify and manage the window within the editor. 858 | 859 | 8. **CanvasGroup**: 860 | - **Type**: `CanvasGroup` 861 | - **Description**: Manages canvas properties such as visibility and interaction. 862 | 863 | 9. **Canvas**: 864 | - **Type**: `Canvas` 865 | - **Description**: The parent canvas of the window. 866 | 867 | 10. **Background**: 868 | - **Type**: `Image` 869 | - **Description**: The background image of the window. 870 | 871 | 11. **IsPointerOver**: 872 | - **Type**: `bool` 873 | - **Description**: Indicates whether the pointer is currently over the window. 874 | 875 | 12. **ViewRoot**: 876 | - **Type**: `RectTransform` 877 | - **Description**: The root transform for the window's view. 878 | 879 | ### Example Usage 880 | 881 | ```csharp 882 | using Battlehub.RTCommon; 883 | using UnityEngine; 884 | 885 | public class CustomRuntimeWindow : RuntimeWindow 886 | { 887 | protected override void OnActivated() 888 | { 889 | base.OnActivated(); 890 | Debug.Log("Window Activated"); 891 | } 892 | 893 | protected override void OnDeactivated() 894 | { 895 | base.OnDeactivated(); 896 | Debug.Log("Window Deactivated"); 897 | } 898 | } 899 | ``` 900 | 901 | ## Built In Windows 902 | 903 | The built-in windows component allows you to set up built-in windows, specify whether a window is a dialog, update their content prefab, tab header, and header text, set startup arguments, and specify the maximum number of windows that can be opened simultaneously. 904 | 905 | ![Built In Windows][builtin_windows] 906 | 907 | The BuiltInWindowNames class provides a list of names for built-in windows in the Runtime Editor. Here are some of the main built-in window names: 908 | 909 | ``` 910 | public static class BuiltInWindowNames 911 | { 912 | public readonly static string Game = RuntimeWindowType.Game.ToString().ToLower(); 913 | public readonly static string Scene = RuntimeWindowType.Scene.ToString().ToLower(); 914 | public readonly static string Hierarchy = RuntimeWindowType.Hierarchy.ToString().ToLower(); 915 | public readonly static string ProjectTree = RuntimeWindowType.ProjectTree.ToString().ToLower(); 916 | public readonly static string ProjectFolder = RuntimeWindowType.ProjectFolder.ToString().ToLower(); 917 | public readonly static string Inspector = RuntimeWindowType.Inspector.ToString().ToLower(); 918 | public readonly static string Console = RuntimeWindowType.Console.ToString().ToLower(); 919 | public readonly static string Animation = RuntimeWindowType.Animation.ToString().ToLower(); 920 | public readonly static string ToolsPanel = RuntimeWindowType.ToolsPanel.ToString().ToLower(); 921 | public readonly static string ImportFile = RuntimeWindowType.ImportFile.ToString().ToLower(); 922 | public readonly static string OpenProject = RuntimeWindowType.OpenProject.ToString().ToLower(); 923 | public readonly static string About = RuntimeWindowType.About.ToString().ToLower(); 924 | public readonly static string SaveFile = RuntimeWindowType.SaveFile.ToString().ToLower(); 925 | public readonly static string OpenFile = RuntimeWindowType.OpenFile.ToString().ToLower(); 926 | public readonly static string SelectColor = RuntimeWindowType.SelectColor.ToString().ToLower(); 927 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 928 | public readonly static string AssetDatabase = "assetdatabase"; 929 | public readonly static string AssetDatabaseSaveScene = "assetdatabasesavescene"; 930 | public readonly static string AssetDatabaseSave = "assetdatabasesave"; 931 | public readonly static string AssetDatabaseSelect = "assetdatabaseselect"; 932 | public readonly static string AssetDatabaseImportSource = "assetdatabaseimportsource"; 933 | public readonly static string AssetDatabaseImport = "assetdatabaseimport"; 934 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 935 | public static string Project => ... 936 | public static string SaveScene => ... 937 | public static string SaveAsset => ... 938 | public static string SelectObject => ... 939 | public static string SelectAssetLibrary => ... 940 | public static string ImportAssets => ... 941 | } 942 | ``` 943 | 944 | 945 | ## Window Manager 946 | 947 | The `IWindowManager` interface provides a comprehensive set of methods for managing windows in the Runtime Editor. It allows you to create, activate, and manage both standard windows and dialog boxes. Below are some examples of how to use the `IWindowManager` to perform various tasks. 948 | 949 | ### Properties and Methods 950 | 951 | The `IWindowManager` interface includes various properties and methods to manage window layouts, components, and dialog boxes: 952 | 953 | #### Properties 954 | 955 | - **bool IsDialogOpened**: Returns true if a dialog is currently opened. 956 | - **RectTransform PopupRoot**: Root panel for popups and floating windows. 957 | - **Transform ComponentsRoot**: Root transform for additional window components. 958 | 959 | #### Methods 960 | 961 | - **void SetDefaultLayout()**: Rebuilds the layout using the default layout builder function. 962 | - **bool RegisterWindow(CustomWindowDescriptor desc)**: Registers a window with a custom descriptor. 963 | - **Transform CreateWindow(string windowTypeName, bool isFree, RegionSplitType splitType, float flexibleSize, Transform parentWindow)**: Creates a window of the specified type. 964 | - **bool IsWindowRegistered(string windowTypeName)**: Determines whether a window of the type is registered. 965 | - **WindowDescriptor GetWindowDescriptor(string windowTypeName, out bool isDialog)**: Gets the window descriptor. 966 | - **string GetWindowTypeName(Transform content)**: Gets the window type name. 967 | - **Transform GetWindow(string windowTypeName)**: Gets the window transform by type. 968 | - **Transform[] GetWindows()**: Gets transforms of all windows. 969 | - **Transform[] GetWindows(string windowTypeName)**: Gets transforms of windows of the specified type. 970 | - **Transform[] GetComponents(Transform content)**: Gets extra components associated with the window. 971 | - **bool Exists(string windowTypeName)**: Checks if a window of the specified type exists. 972 | - **bool IsActive(string windowTypeName)**: Checks if the window of the specified type is active. 973 | - **bool IsActive(Transform content)**: Checks if the window is active. 974 | - **Transform FindPointerOverWindow(RuntimeWindow exceptWindow)**: Finds the uppermost window to which the pointer is pointing. 975 | - **bool ActivateWindow(Transform content)**: Activates the window of the specified content. 976 | - **void SetWindowArgs(Transform content, string args)**: Sets window arguments. 977 | - **void DestroyWindow(Transform content)**: Destroys the specified window. 978 | - **void DestroyWindowsOfType(string windowTypeName)**: Destroys windows of the specified type. 979 | - **void DestroyDialogWindow()**: Destroys the topmost dialog window. 980 | - **void MessageBox(string header, string text, DialogAction<DialogCancelArgs> ok = null)**: Creates a message box. 981 | 982 | 983 | > **Note** <br/> 984 | For a complete list of properties and methods, refer to the IWindowManager interface definition. 985 | 986 | ### Getting the Window Manager 987 | 988 | To access the `IWindowManager`, use the `IOC.Resolve<IWindowManager>()` method: 989 | 990 | ```csharp 991 | using Battlehub.RTCommon; 992 | using Battlehub.RTEditor; 993 | using UnityEngine; 994 | 995 | public class GetWindowManager : MonoBehaviour 996 | { 997 | void Start() 998 | { 999 | IWindowManager wm = IOC.Resolve<IWindowManager>(); 1000 | } 1001 | } 1002 | ``` 1003 | 1004 | ### Showing a Message Box 1005 | 1006 | You can display a message box with a header and text, and handle the OK button click event: 1007 | 1008 | ```csharp 1009 | wm.MessageBox("Header", "Text", (sender, args) => 1010 | { 1011 | Debug.Log("OK Click"); 1012 | }); 1013 | ``` 1014 | 1015 | ### Showing a Confirmation Dialog 1016 | 1017 | You can display a confirmation dialog with custom buttons and handle the Yes and No button click events: 1018 | 1019 | ```csharp 1020 | wm.Confirmation("Header", "Text", 1021 | (sender, args) => 1022 | { 1023 | Debug.Log("Yes click"); 1024 | }, 1025 | (sender, args) => 1026 | { 1027 | Debug.Log("No click"); 1028 | }, 1029 | "Yes", "No"); 1030 | ``` 1031 | 1032 | ### Activating a Window 1033 | 1034 | To activate an existing window, use the `ActivateWindow` method with the name of the window: 1035 | 1036 | ```csharp 1037 | wm.ActivateWindow(BuiltInWindowNames.Scene); 1038 | ``` 1039 | 1040 | ### Creating a Window 1041 | 1042 | To create a new window, use the `CreateWindow` method with the name of the window: 1043 | 1044 | ```csharp 1045 | wm.CreateWindow(BuiltInWindowNames.Scene); 1046 | ``` 1047 | 1048 | ### Creating a Dialog Window 1049 | 1050 | To create a dialog window, use the `CreateDialogWindow` method with the name of the window and handle the OK and Cancel button click events: 1051 | 1052 | ```csharp 1053 | IWindowManager wm = IOC.Resolve<IWindowManager>(); 1054 | wm.CreateDialogWindow(BuiltInWindowNames.About, "Header", 1055 | (sender, args) => { Debug.Log("OK"); }, 1056 | (sender, args) => { Debug.Log("Cancel"); }); 1057 | ``` 1058 | 1059 | > **Note** <br/> 1060 | For examples, see **Scene8 - Window Management** in the [Example Scenes section](#example-scenes). 1061 | 1062 | ## Main and Context Menu 1063 | 1064 | The Runtime Editor uses the Menu control to implement both the main menu and context menus. To extend the main menu, you can create a static class with the **[MenuDefinition]** attribute and add static methods with the **[MenuCommand]** attribute. 1065 | 1066 | ### Extending the Main Menu 1067 | 1068 | To add new commands or modify existing ones, follow the steps below: 1069 | 1070 | 1. Create a static class with the **[MenuDefinition]** attribute. 1071 | 2. Add static methods with the **[MenuCommand]** attribute to define new menu items or modify existing ones. 1072 | 1073 | ```csharp 1074 | using Battlehub.RTCommon; 1075 | using Battlehub.RTEditor; 1076 | using Battlehub.UIControls.MenuControl; 1077 | using UnityEngine; 1078 | 1079 | [MenuDefinition] 1080 | public static class MyMenu 1081 | { 1082 | // Add new command to existing menu 1083 | [MenuCommand("MenuWindow/Create My Window")] 1084 | public static void CreateMyWindow() 1085 | { 1086 | Debug.Log("Create My Window"); 1087 | } 1088 | 1089 | // Add new command to new menu 1090 | [MenuCommand("My Menu/My Submenu/My Command")] 1091 | public static void CreateMyMenu() 1092 | { 1093 | Debug.Log("Create My Menu"); 1094 | } 1095 | 1096 | // Disable menu item 1097 | [MenuCommand("My Menu/My Submenu/My Command", validate: true)] 1098 | public static bool ValidateMyCommand() 1099 | { 1100 | Debug.Log("Disable My Command"); 1101 | return false; 1102 | } 1103 | 1104 | // Replace existing menu item 1105 | [MenuCommand("MenuFile/Close")] 1106 | public static void Close() 1107 | { 1108 | Debug.Log("Intercepted"); 1109 | 1110 | IRuntimeEditor rte = IOC.Resolve<IRuntimeEditor>(); 1111 | rte.Close(); 1112 | } 1113 | 1114 | // Hide existing menu item 1115 | [MenuCommand("MenuHelp/About RTE", hide: true)] 1116 | public static void HideAbout() { } 1117 | } 1118 | ``` 1119 | 1120 | ![Main Menu][main_menu] 1121 | 1122 | ### Instance Methods as Menu Commands 1123 | 1124 | It is possible to use instance methods as menu commands. For this to work, the corresponding MonoBehaviour must exist in the scene. 1125 | 1126 | ```csharp 1127 | using Battlehub.UIControls.MenuControl; 1128 | using UnityEngine; 1129 | 1130 | [MenuDefinition] 1131 | public class MyMenu : MonoBehaviour 1132 | { 1133 | // Add new command to new menu 1134 | [MenuCommand("My Menu/My Submenu/My Command")] 1135 | public void CreateMyMenu() 1136 | { 1137 | Debug.Log("Create My Menu"); 1138 | } 1139 | 1140 | // Disable menu item 1141 | [MenuCommand("My Menu/My Submenu/My Command", validate: true)] 1142 | public bool ValidateMyCommand() 1143 | { 1144 | Debug.Log("Disable My Command"); 1145 | return false; 1146 | } 1147 | } 1148 | ``` 1149 | 1150 | ### MenuDefinition with the sceneName parameter 1151 | Use the [MenuDefinition] attribute with the sceneName parameter to specify the scene. This way, the menu will only exist in the Unity scene with the corresponding name. 1152 | ```csharp 1153 | using Battlehub.UIControls.MenuControl; 1154 | using UnityEngine; 1155 | 1156 | [MenuDefinition(sceneName:"Scene")] 1157 | public class MyMenu : MonoBehaviour 1158 | { 1159 | 1160 | // Add new command to new menu 1161 | [MenuCommand("My Menu/My Submenu/My Command")] 1162 | public void CreateMyMenu() 1163 | { 1164 | Debug.Log("Create My Menu"); 1165 | } 1166 | } 1167 | ``` 1168 | 1169 | > **Note** <br/> 1170 | For examples, see **Scene9 - Extending Main Menu** in the [Example Scenes section](#example-scenes). 1171 | 1172 | ### Opening a Context Menu with Custom Commands 1173 | 1174 | The `IContextMenuModel` interface defines the structure for managing context menus within the runtime editor. It provides methods and events to create, open, and manage context menus dynamically. 1175 | 1176 | #### Events 1177 | 1178 | - **event EventHandler<ContextMenuArgs> Open**: Event triggered when the context menu is opened. The `ContextMenuArgs` provides details about the menu items and their context. 1179 | - **event EventHandler Close**: Event triggered when the context menu is closed. 1180 | 1181 | #### Methods 1182 | 1183 | - **void RaiseOpen(ContextMenuArgs args)**: Raises the `Open` event with the specified context menu arguments. 1184 | - **void Show()**: Shows an empty context menu. It is possible to add menu items in Open event handler. 1185 | - **void Show(params ContextMenuItem[] items)**: Shows a context menu with the specified items. 1186 | - **void Show(ContextMenuArgs args)**: Shows a context menu using the specified `ContextMenuArgs`. The `ContextMenuArgs` includes details about the menu items and their context. 1187 | 1188 | To open a context menu with custom commands, follow these steps: 1189 | 1190 | 1. Define a class with methods to open the context menu. 1191 | 2. Use the **IContextMenuModel** interface to create and show the context menu with the defined commands. 1192 | 1193 | ```csharp 1194 | using Battlehub.RTCommon; 1195 | using Battlehub.RTEditor; 1196 | using Battlehub.RTEditor.Models; 1197 | using UnityEngine; 1198 | 1199 | public class MyContextMenu : MonoBehaviour 1200 | { 1201 | private void Update() 1202 | { 1203 | if (Input.GetKeyDown(KeyCode.Space)) 1204 | { 1205 | OpenContextMenu(); 1206 | } 1207 | } 1208 | 1209 | public void OpenContextMenu() 1210 | { 1211 | IContextMenuModel contextMenu = IOC.Resolve<IContextMenuModel>(); 1212 | 1213 | contextMenu.Show( 1214 | new ContextMenuItem 1215 | { 1216 | Path = "My Command 1", 1217 | Action = arg => 1218 | { 1219 | Debug.Log("Run My Command1"); 1220 | 1221 | var editor = IOC.Resolve<IRuntimeEditor>(); 1222 | Debug.Log(editor.Selection.activeGameObject); 1223 | } 1224 | }, 1225 | new ContextMenuItem 1226 | { 1227 | Path = "My Command 2", 1228 | Action = arg => Debug.Log("Run My Command2"), 1229 | Validate = arg => arg.IsValid = false 1230 | }); 1231 | } 1232 | } 1233 | ``` 1234 | 1235 | Basically, the same can be done using the following syntax: 1236 | 1237 | ``` 1238 | using Battlehub.RTCommon; 1239 | using Battlehub.RTEditor; 1240 | using Battlehub.RTEditor.Models; 1241 | using UnityEngine; 1242 | 1243 | public class MyContextMenu : MonoBehaviour 1244 | { 1245 | private void Update() 1246 | { 1247 | if (Input.GetKeyDown(KeyCode.Space)) 1248 | { 1249 | OpenContextMenu(); 1250 | } 1251 | } 1252 | 1253 | public void OpenContextMenu() 1254 | { 1255 | var contextMenu = IOC.Resolve<IContextMenuModel>(); 1256 | contextMenu.Open += OnContextMenuOpen; 1257 | contextMenu.Show(); 1258 | contextMenu.Open -= OnContextMenuOpen; 1259 | } 1260 | 1261 | private void OnContextMenuOpen(object sender, ContextMenuArgs e) 1262 | { 1263 | if (e.WindowName == BuiltInWindowNames.Scene) 1264 | { 1265 | e.AddMenuItem("My Command 1", arg => 1266 | { 1267 | Debug.Log("Run My Command1"); 1268 | var editor = IOC.Resolve<IRuntimeEditor>(); 1269 | Debug.Log(editor.Selection.activeGameObject); 1270 | }); 1271 | 1272 | e.AddMenuItem("My Command 2", 1273 | arg => Debug.Log("Run My Command2"), 1274 | arg => arg.IsValid = false); 1275 | } 1276 | } 1277 | } 1278 | ``` 1279 | 1280 | ![Context Menu][context_menu] 1281 | 1282 | > **Note** <br/> 1283 | For examples, see **Scene10 - Extending Context Menu** in the [Example Scenes section](#example-scenes). 1284 | 1285 | 1286 | ## Editor Extension 1287 | The `EditorExtension` class serves as a recommended base class for writing Runtime Editor Extensions. It provides two useful methods that can be overridden: `OnInit()` and `OnCleanup()`. These methods are intended for initializing and cleaning up the extension, respectively. 1288 | 1289 | ### Methods 1290 | 1291 | - **void OnInit()**: This method is called when the extension is initialized. You should override this method to write the extension initialization code. 1292 | - **void OnCleanup()**: This method is called when the extension is being cleaned up. You should override this method to write the extension cleanup code. 1293 | 1294 | ### Example 1295 | 1296 | ```csharp 1297 | using Battlehub.RTCommon; 1298 | using Battlehub.RTEditor; 1299 | 1300 | public class EditorExtensionExample : EditorExtension 1301 | { 1302 | protected override void OnInit() 1303 | { 1304 | var editor = IOC.Resolve<IRuntimeEditor>(); 1305 | 1306 | // Do extension initialization here 1307 | Debug.Log("Editor extension initialized."); 1308 | } 1309 | 1310 | protected override void OnCleanup() 1311 | { 1312 | // Do extension cleanup here 1313 | Debug.Log("Editor extension cleaned up."); 1314 | } 1315 | } 1316 | ``` 1317 | 1318 | ### Additional Convenience Extension Base Classes 1319 | 1320 | There are three additional convenience extension base classes: 1321 | 1322 | 1. **LayoutExtension** 1323 | 2. **RuntimeWindowExtension** 1324 | 3. **SceneComponentExtension** 1325 | 1326 | #### LayoutExtension 1327 | 1328 | The `LayoutExtension` class provides four additional methods for handling window registration, layout building, and events for additional handling before and after the layout. 1329 | 1330 | ##### Methods 1331 | 1332 | - **void OnRegisterWindows(IWindowManager wm)**: Registers custom windows. 1333 | - **void OnBeforeBuildLayout(IWindowManager wm)**: Invoked before the layout is built. 1334 | - **void OnAfterBuildLayout(IWindowManager wm)**: Invoked after the layout is built. 1335 | - **LayoutInfo GetLayoutInfo(IWindowManager wm)**: Provides layout information. 1336 | 1337 | ##### Example 1338 | 1339 | ```csharp 1340 | using Battlehub.RTCommon; 1341 | using Battlehub.RTEditor; 1342 | using UnityEngine; 1343 | 1344 | public class LayoutExtensionExample : LayoutExtension 1345 | { 1346 | [SerializeField] 1347 | private GameObject m_sceneWindow = null; 1348 | 1349 | protected override void OnInit() 1350 | { 1351 | } 1352 | 1353 | protected override void OnCleanup() 1354 | { 1355 | } 1356 | 1357 | protected override void OnRegisterWindows(IWindowManager wm) 1358 | { 1359 | if (m_sceneWindow != null) 1360 | { 1361 | wm.OverrideWindow(BuiltInWindowNames.Scene, m_sceneWindow); 1362 | } 1363 | } 1364 | 1365 | protected override void OnBeforeBuildLayout(IWindowManager wm) 1366 | { 1367 | // Hide header toolbar 1368 | wm.OverrideTools(null); 1369 | } 1370 | 1371 | protected override void OnAfterBuildLayout(IWindowManager wm) 1372 | { 1373 | base.OnAfterBuildLayout(wm); 1374 | } 1375 | 1376 | protected override LayoutInfo GetLayoutInfo(IWindowManager wm) 1377 | { 1378 | // Initializing a layout with one window - Scene 1379 | LayoutInfo layoutInfo = wm.CreateLayoutInfo(BuiltInWindowNames.Scene); 1380 | layoutInfo.IsHeaderVisible = false; 1381 | 1382 | return layoutInfo; 1383 | } 1384 | } 1385 | ``` 1386 | 1387 | #### RuntimeWindowExtension 1388 | 1389 | The `RuntimeWindowExtension` class calls `Extend` and `Cleanup` methods for a specific window type. 1390 | 1391 | ##### Methods 1392 | 1393 | - **void Extend(RuntimeWindow window)**: Extends the specified window. 1394 | - **void Cleanup(RuntimeWindow window)**: Cleans up the specified window. 1395 | 1396 | ##### Example 1397 | 1398 | ```csharp 1399 | using Battlehub.RTCommon; 1400 | using Battlehub.RTEditor; 1401 | using UnityEngine; 1402 | 1403 | public class RuntimeWindowExtensionExample : RuntimeWindowExtension 1404 | { 1405 | [SerializeField] 1406 | private RectTransform m_layer = null; 1407 | 1408 | public override string WindowTypeName => BuiltInWindowNames.Scene; 1409 | 1410 | protected override void Extend(RuntimeWindow window) 1411 | { 1412 | Instantiate(m_layer, window.ViewRoot).Stretch(); 1413 | } 1414 | 1415 | protected override void Cleanup(RuntimeWindow window) 1416 | { 1417 | base.Cleanup(window); 1418 | } 1419 | } 1420 | ``` 1421 | 1422 | #### SceneComponentExtension 1423 | 1424 | The `SceneComponentExtension` class allows you to extend the scene view when it is activated and perform cleanup when it is deactivated. 1425 | 1426 | ##### Methods 1427 | 1428 | - **void OnSceneActivated(IRuntimeSceneComponent sceneComponent)**: Called when the scene is activated. 1429 | - **void OnSceneDeactivated(IRuntimeSceneComponent sceneComponent)**: Called when the scene is deactivated. 1430 | 1431 | ##### Example 1432 | 1433 | ```csharp 1434 | using Battlehub.RTCommon; 1435 | using Battlehub.RTEditor; 1436 | using Battlehub.RTHandles; 1437 | using UnityEngine; 1438 | 1439 | public class SceneComponentExtensionExample : SceneComponentExtension 1440 | { 1441 | protected override void OnSceneActivated(IRuntimeSceneComponent sceneComponent) 1442 | { 1443 | base.OnSceneActivated(sceneComponent); 1444 | 1445 | sceneComponent.PositionHandle.Drag.AddListener(OnDrag); 1446 | } 1447 | 1448 | protected override void OnSceneDeactivated(IRuntimeSceneComponent sceneComponent) 1449 | { 1450 | base.OnSceneDeactivated(sceneComponent); 1451 | 1452 | sceneComponent.PositionHandle.Drag.RemoveListener(OnDrag); 1453 | } 1454 | 1455 | private void OnDrag(BaseHandle handle) 1456 | { 1457 | foreach (Transform target in handle.ActiveTargets) 1458 | { 1459 | Vector3 p = target.position; 1460 | target.position = new Vector3(Mathf.Round(p.x), Mathf.Round(p.y), Mathf.Round(p.z)); 1461 | } 1462 | } 1463 | } 1464 | ``` 1465 | 1466 | ## Window 1467 | 1468 | ![Window][window] 1469 | 1470 | A typical Runtime Editor Window consists of several parts, including a window prefab with a `RuntimeWindow` component, a frame, background, and view. 1471 | It also incorporates a View, ViewModel, and ViewBinding, built on top of `UnityWeld.dll`. 1472 | This structure ensures a modular and flexible design, allowing for a seamless integration of UI components and binding logic. 1473 | 1474 | #### View-ViewModel-ViewBinding Architecture 1475 | The window uses the Model-View-ViewModel (MVVM) pattern, facilitated by `UnityWeld.dll`, to ensure a clear separation of concerns and enhance maintainability. 1476 | 1. **View**: Contains the UI elements and visual components. 1477 | 2. **ViewModel**: Holds the logic and data for the view, acting as an intermediary between the view and the model. 1478 | 3. **ViewBinding**: Binds the view to the ViewModel, ensuring that changes in the ViewModel are reflected in the view and vice versa. 1479 | 1480 | ### UnityWeld 1481 | https://github.com/Battlehub0x/Unity-Weld?tab=readme-ov-file 1482 | 1483 | ### View 1484 | 1485 | The `View` class in the Runtime Editor framework is responsible for managing the visual representation and interaction logic of editor windows. It handles various events related to dragging, dropping, activating, and deactivating, and it provides mechanisms for binding the view to a `ViewModel`. 1486 | 1487 | #### Key Properties 1488 | 1489 | 1. **DragObjects**: An enumerable property that holds the objects being dragged. 1490 | 2. **CanDropExternalObjects**: A boolean property indicating whether external drag objects can be dropped. 1491 | 3. **IsDraggingOver**: A boolean property indicating whether a drag operation is currently over the view. 1492 | 4. **ViewInput**: A protected property providing access to the `ViewInput` component. 1493 | 5. **Window**: A protected property providing access to the associated `RuntimeWindow`. 1494 | 6. **Editor**: A protected property providing access to the `IRTE` instance. 1495 | 1496 | #### Key Events 1497 | 1498 | 1. **SelectAll**: An event triggered to select all items. 1499 | 2. **Duplicate**: An event triggered to duplicate items. 1500 | 3. **Delete**: An event triggered to delete items. 1501 | 4. **DragEnter**: An event triggered when a drag operation enters the view. 1502 | 5. **DragLeave**: An event triggered when a drag operation leaves the view. 1503 | 6. **Drag**: An event triggered during a drag operation. 1504 | 7. **Drop**: An event triggered when a drop operation occurs. 1505 | 8. **DragObjectsChanged**: An event triggered when the `DragObjects` property changes. 1506 | 9. **Activated**: An event triggered when the view is activated. 1507 | 10. **Deactivated**: An event triggered when the view is deactivated. 1508 | 1509 | #### Key Methods 1510 | 1511 | 1. **Awake()**: Initializes the view, setting up event handlers for the associated `RuntimeWindow`. 1512 | 2. **OnEnable()**: Called when the view is enabled. 1513 | 3. **Start()**: Ensures the `ViewInput` component is present. 1514 | 4. **OnDisable()**: Called when the view is disabled. 1515 | 5. **OnDestroy()**: Cleans up event handlers and references when the view is destroyed. 1516 | 6. **OnDragEnter(PointerEventData)**: Handles the drag enter event, updating the `DragObjects` property and setting the cursor. 1517 | 7. **OnDragLeave(PointerEventData)**: Handles the drag leave event, resetting the `DragObjects` property and setting the cursor. 1518 | 8. **OnDrag(PointerEventData)**: Handles the drag event. 1519 | 9. **OnDrop(PointerEventData)**: Handles the drop event, resetting the `DragObjects` property. 1520 | 10. **OnActivated(object, EventArgs)**: Handles the activation event. 1521 | 11. **OnDeactivated(object, EventArgs)**: Handles the deactivation event. 1522 | 1523 | #### Utility Methods 1524 | 1525 | 1. **ReplaceWith<T>(Component, bool)**: Replaces the current `View` component with a new one of type `T`. 1526 | 2. **ReplaceWith<T>(GameObject, bool)**: Replaces the current `View` component on the specified `GameObject` with a new one of type `T`. 1527 | 3. **ReplaceWith(Type, Component, bool)**: Replaces the current `View` component with a new one of the specified type. 1528 | 4. **ReplaceWith(Type, GameObject, bool)**: Replaces the current `View` component on the specified `GameObject` with a new one of the specified type. 1529 | 1530 | #### Example Usage 1531 | 1532 | ```csharp 1533 | using Battlehub.RTCommon; 1534 | using Battlehub.RTEditor.Views; 1535 | using UnityEngine; 1536 | using UnityEngine.EventSystems; 1537 | 1538 | public class CustomView : View 1539 | { 1540 | protected override void Awake() 1541 | { 1542 | base.Awake(); 1543 | Debug.Log("CustomView Awake"); 1544 | } 1545 | 1546 | protected override void OnEnable() 1547 | { 1548 | base.OnEnable(); 1549 | Debug.Log("CustomView OnEnable"); 1550 | } 1551 | 1552 | protected override void OnDisable() 1553 | { 1554 | base.OnDisable(); 1555 | Debug.Log("CustomView OnDisable"); 1556 | } 1557 | 1558 | protected override void OnDragEnter(PointerEventData pointerEventData) 1559 | { 1560 | base.OnDragEnter(pointerEventData); 1561 | Debug.Log("Drag entered CustomView"); 1562 | } 1563 | 1564 | protected override void OnDrop(PointerEventData pointerEventData) 1565 | { 1566 | base.OnDrop(pointerEventData); 1567 | Debug.Log("Dropped on CustomView"); 1568 | } 1569 | } 1570 | ``` 1571 | 1572 | 1573 | 1574 | ### ViewModel 1575 | 1576 | The `ViewModel` base class is a foundational component in the MVVM architecture used within the Runtime Editor. It provides essential properties and methods to facilitate the interaction between the view and the underlying data and logic. This class inherits from `ViewModelBase` and uses the `UnityWeld.Binding` attribute for data binding. 1577 | 1578 | #### Key Properties 1579 | 1580 | 1. **CanDropExternalObjects**: A boolean property indicating whether external drag objects can be dropped. It uses `RaisePropertyChanged` to notify the view of any changes. 1581 | 2. **ExternalDragObjects**: An enumerable property that holds the external drag objects. 1582 | 3. **Editor**: Provides access to the `IRuntimeEditor` instance. 1583 | 4. **Localization**: Provides access to the `ILocalization` instance. 1584 | 5. **WindowManager**: Provides access to the `IWindowManager` instance. 1585 | 6. **Selection**: Represents the current runtime selection. 1586 | 7. **Undo**: Represents the undo functionality. 1587 | 8. **SelectionOverride**: Allows overriding the default selection behavior. 1588 | 9. **UndoOverride**: Allows overriding the default undo behavior. 1589 | 10. **WindowName**: The name of the window associated with this ViewModel. 1590 | 1591 | #### Key Methods 1592 | 1593 | 1. **Awake()**: Initializes the `Editor`, `Localization`, and `WindowManager` properties and sets the `WindowName` if the ViewModel is attached to a `RuntimeWindow`. 1594 | 2. **OnEnable()**: Sets up the `Selection` and `Undo` properties. 1595 | 3. **OnDisable()**: Called when the ViewModel is disabled. 1596 | 4. **OnDestroy()**: Cleans up references to the `Editor`, `Localization`, `WindowManager`, `Selection`, and `Undo`. 1597 | 5. **SetBusy()**: Sets the editor to a busy state. 1598 | 6. **OnActivated()**: Called when the ViewModel is activated. 1599 | 7. **OnDeactivated()**: Called when the ViewModel is deactivated. 1600 | 8. **OnSelectAll()**: Handles the select all action. 1601 | 9. **OnDelete()**: Handles the delete action. 1602 | 10. **OnDuplicate()**: Handles the duplicate action. 1603 | 11. **OnExternalObjectEnter()**: Called when an external object enters the drop area. 1604 | 12. **OnExternalObjectLeave()**: Called when an external object leaves the drop area. 1605 | 13. **OnExternalObjectDrag()**: Called during the dragging of an external object. 1606 | 14. **OnExternalObjectDrop()**: Called when an external object is dropped. 1607 | 1608 | 15. **RaisePropertyChanged(string propertyName)**: 1609 | - Notifies listeners that a property value has changed. 1610 | - **Parameter**: `propertyName` (string) - The name of the property that changed. 1611 | 1612 | 16. **RaisePropertyChanged(PropertyChangedEventArgs args)**: 1613 | - Notifies listeners that a property value has changed using `PropertyChangedEventArgs`. 1614 | - **Parameter**: `args` (PropertyChangedEventArgs) - The event arguments containing the property name. 1615 | 1616 | 17. **ReplaceWith<T>(UnityEngine.Component component) where T : ViewModelBase**: 1617 | - Replaces the current `ViewModelBase` component on the specified component's game object with a new one of type `T`. 1618 | - **Parameter**: `component` (UnityEngine.Component) - The component whose game object's view model will be replaced. 1619 | 1620 | 18. **ReplaceWith<T>(GameObject go) where T : ViewModelBase**: 1621 | - Replaces the current `ViewModelBase` component on the specified game object with a new one of type `T`. 1622 | - **Parameter**: `go` (GameObject) - The game object whose view model will be replaced. 1623 | 1624 | 19. **ReplaceWith(Type type, UnityEngine.Component component)**: 1625 | - Replaces the current `ViewModelBase` component on the specified component's game object with a new one of the specified type. 1626 | - **Parameter**: `type` (Type) - The type of the new view model. 1627 | - **Parameter**: `component` (UnityEngine.Component) - The component whose game object's view model will be replaced. 1628 | 1629 | 20. **ReplaceWith(Type type, GameObject go)**: 1630 | - Replaces the current `ViewModelBase` component on the specified game object with a new one of the specified type. 1631 | - **Parameter**: `type` (Type) - The type of the new view model. 1632 | - **Parameter**: `go` (GameObject) - The game object whose view model will be replaced. 1633 | 1634 | #### Example Usage 1635 | 1636 | ```csharp 1637 | [Binding] 1638 | public class MyViewModel : ViewModel 1639 | { 1640 | private bool m_isEnabled; 1641 | 1642 | [Binding] 1643 | public bool IsEnabled 1644 | { 1645 | get { return m_isEnabled; } 1646 | set 1647 | { 1648 | if (m_isEnabled != value) 1649 | { 1650 | m_isEnabled = value; 1651 | RaisePropertyChanged(nameof(IsEnabled)); 1652 | } 1653 | } 1654 | } 1655 | 1656 | protected override void OnActivated() 1657 | { 1658 | base.OnActivated(); 1659 | Debug.Log("ViewModel activated."); 1660 | } 1661 | 1662 | protected override void OnDeactivated() 1663 | { 1664 | base.OnDeactivated(); 1665 | Debug.Log("ViewModel deactivated."); 1666 | } 1667 | 1668 | [Binding] 1669 | public override void OnSelectAll() 1670 | { 1671 | base.OnSelectAll(); 1672 | Debug.Log("Select All action triggered."); 1673 | } 1674 | 1675 | [Binding] 1676 | public override void OnDelete() 1677 | { 1678 | base.OnDelete(); 1679 | Debug.Log("Delete action triggered."); 1680 | } 1681 | } 1682 | ``` 1683 | 1684 | ### HierarchicalDataViewModel 1685 | 1686 | The `HierarchicalDataViewModel<T>` class is a base view model class used for managing hierarchical data structures in the Runtime Editor, such as those found in Hierarchy and Project windows. 1687 | This class provides mechanisms for managing tree-like data structures and handling common operations like selection, drag-and-drop, and context menus. 1688 | 1689 | #### Key Properties 1690 | 1691 | - **DataSource**: Gets the current instance as the data source for hierarchical data binding. 1692 | - **SelectedItem**: Gets or sets the currently selected item. 1693 | - **SelectedItems**: Gets or sets the collection of currently selected items. 1694 | - **CanDropItems**: Indicates whether items can be dropped into the current target. 1695 | - **SourceItems**: Gets or sets the items that are being dragged. 1696 | - **TargetItem**: Gets or sets the item that is the current drop target. 1697 | - **ContextMenu**: Gets or sets the collection of context menu items. 1698 | 1699 | #### Key Events 1700 | 1701 | - **HierarchicalDataChanged**: Raised when the hierarchical data changes. 1702 | - **ContextMenuOpened**: Raised when the context menu is opened. 1703 | 1704 | #### Key Methods 1705 | 1706 | 1. **RaiseHierarchicalDataChanged**: Raises the `HierarchicalDataChanged` event with the specified arguments. 1707 | 2. **RaiseItemAdded**: Raises the `HierarchicalDataChanged` event to indicate an item was added. 1708 | 3. **RaiseItemInserted**: Raises the `HierarchicalDataChanged` event to indicate an item was inserted. 1709 | 4. **RaiseItemRemoved**: Raises the `HierarchicalDataChanged` event to indicate an item was removed. 1710 | 5. **RaiseRemoveSelected**: Raises the `HierarchicalDataChanged` event to indicate selected items should be removed. 1711 | 6. **RaiseNextSiblingChanged**: Raises the `HierarchicalDataChanged` event to indicate the next sibling of an item has changed. 1712 | 7. **RaisePrevSiblingChanged**: Raises the `HierarchicalDataChanged` event to indicate the previous sibling of an item has changed. 1713 | 8. **RaiseParentChanged**: Raises the `HierarchicalDataChanged` event to indicate the parent of an item has changed. 1714 | 9. **RaiseExpand**: Raises the `HierarchicalDataChanged` event to indicate an item should be expanded. 1715 | 10. **RaiseCollapse**: Raises the `HierarchicalDataChanged` event to indicate an item should be collapsed. 1716 | 11. **RaiseSelect**: Raises the `HierarchicalDataChanged` event to select items. 1717 | 12. **RaiseReset**: Raises the `HierarchicalDataChanged` event to reset the hierarchical data. 1718 | 13. **RaiseDataBindVisible**: Raises the `HierarchicalDataChanged` event to rebind the visible data. 1719 | 1720 | #### Context Menu Handling 1721 | 1722 | 1. **GetContextMenuAnchor**: Returns the context menu anchor, which includes the target item and selected items. 1723 | 2. **OpenContextMenu**: Opens the context menu and populates it with items. 1724 | 3. **OnContextMenu**: Method for adding items to the context menu. Override this method to customize the context menu. 1725 | 1726 | #### IHierarchicalData Implementation 1727 | 1728 | 1. **GetChildren**: Returns the children of a given parent item. 1729 | 2. **GetFlags**: Returns the hierarchical data flags. 1730 | 3. **GetItemFlags**: Returns the flags for a specific item. 1731 | 4. **GetParent**: Returns the parent of a given item. 1732 | 5. **HasChildren**: Indicates whether a given item has children. 1733 | 6. **IndexOf**: Returns the index of a child item within its parent. 1734 | 7. **Expand**: Expands a given item. 1735 | 8. **Collapse**: Collapses a given item. 1736 | 9. **Select**: Selects the specified items. 1737 | 1738 | #### Bound UnityEvent Handlers 1739 | 1740 | 1. **OnItemsBeginDrag**: Called when items begin to be dragged. 1741 | 2. **OnItemDragEnter**: Called when an item drag enters the view. 1742 | 3. **OnItemDragLeave**: Called when an item drag leaves the view. 1743 | 4. **OnItemsDrag**: Called when items are being dragged. 1744 | 5. **OnItemsSetLastChild**: Called to set the last child during drag-and-drop. 1745 | 6. **OnItemsSetNextSibling**: Called to set the next sibling during drag-and-drop. 1746 | 7. **OnItemsSetPrevSibling**: Called to set the previous sibling during drag-and-drop. 1747 | 8. **OnItemsCancelDrop**: Called to cancel the drop operation. 1748 | 9. **OnItemsBeginDrop**: Called when items begin to be dropped. 1749 | 10. **OnItemsDrop**: Called when items are dropped. 1750 | 11. **OnItemsEndDrag**: Called when items finish being dragged. 1751 | 12. **OnItemsRemoved**: Called when items are removed. 1752 | 13. **OnItemBeginEdit**: Called when an item begins editing. 1753 | 14. **OnItemEndEdit**: Called when an item ends editing. 1754 | 15. **OnItemHold**: Called when an item is held. 1755 | 16. **OnItemClick**: Called when an item is clicked. 1756 | 17. **OnItemDoubleClick**: Called when an item is double-clicked. 1757 | 18. **OnHold**: Called when the view is held. 1758 | 19. **OnClick**: Called when the view is clicked. 1759 | 1760 | #### Example Usage 1761 | 1762 | ```csharp 1763 | using Battlehub.RTEditor.ViewModels; 1764 | using System.Collections.Generic; 1765 | using UnityWeld.Binding; 1766 | 1767 | [Binding] 1768 | public class MyHierarchicalDataViewModel : HierarchicalDataViewModel<MyItemType> 1769 | { 1770 | public override IEnumerable<MyItemType> GetChildren(MyItemType parent) 1771 | { 1772 | // Return children of the parent item 1773 | } 1774 | 1775 | public override void OnContextMenu(List<MenuItemViewModel> menuItems) 1776 | { 1777 | // Add custom menu items 1778 | menuItems.Add(new MenuItemViewModel 1779 | { 1780 | Path = "Custom Command", 1781 | Action = cmd => Debug.Log("Custom command executed") 1782 | }); 1783 | } 1784 | 1785 | protected override void OnSelectedItemsChanged(IEnumerable<MyItemType> unselectedObjects, IEnumerable<MyItemType> selectedObjects) 1786 | { 1787 | // Handle selection change 1788 | } 1789 | } 1790 | ``` 1791 | 1792 | ## Custom windows 1793 | To create a custom window, follow these steps: 1794 | 1795 | 1. **Click Tools -> Runtime Editor -> Create Custom Window** 1796 | 1797 | 2. **Enter the Name** 1798 | - Enter `MyCustomWindow.prefab` and click **Save**. 1799 | 1800 | 3. **Enter the Namespace** 1801 | - Enter `MyNamespace` namespace and click **OK**. 1802 | 1803 | 4. **Add RegisterMyCustomWindow Component** 1804 | - Add the `RegisterMyCustomWindow` component created in step 1 to a GameObject in the scene. 1805 | 1806 | 5. **Drag & Drop Prefab** 1807 | - Drag & drop `MyCustomWindow.prefab` to the Prefab field of the `RegisterMyCustomWindow` script. 1808 | 1809 | 6. **Click Play** 1810 | - Click **Play** in the Unity Editor. 1811 | 1812 | 7. **Open Custom Window** 1813 | - You should be able to open the custom window using the main menu. 1814 | 1815 | ![Click Create Custom Window][custom_window_1] 1816 | ![Register My Custom Window][custom_window_2] 1817 | ![Register My Custom Window][custom_window_3] 1818 | 1819 | 1820 | ## Extending Existing Windows 1821 | 1822 | There are several possible approaches for extending existing windows in the Runtime Editor. 1823 | 1824 | ### 1. Override the Window Prefab 1825 | 1826 | First, locate the window prefab you want to override or extend. Create a prefab variant and set the corresponding field in the `BuiltInWindows` component to your custom window. 1827 | ![Extending Window By Overriding Prefab][extending_window_1] 1828 | 1829 | ### 2. Override Programmatically Using Layout Extension 1830 | 1831 | You can also achieve this programmatically using a `LayoutExtension`. 1832 | 1833 | ```csharp 1834 | using Battlehub.RTEditor; 1835 | using UnityEngine; 1836 | 1837 | public class MyLayoutExtension : LayoutExtension 1838 | { 1839 | [SerializeField] 1840 | private GameObject m_myHierarchyWindow; 1841 | 1842 | protected override void OnRegisterWindows(IWindowManager wm) 1843 | { 1844 | wm.OverrideWindow(BuiltInWindowNames.Hierarchy, 1845 | new WindowDescriptor { ContentPrefab = m_myHierarchyWindow }); 1846 | } 1847 | } 1848 | ``` 1849 | 1850 | ### 3. Override ViewModel or View Using RuntimeWindowExtension and ReplaceWith Method 1851 | 1852 | You can override the `ViewModel` or `View` of a window using a `RuntimeWindowExtension` and the `ReplaceWith` method. 1853 | 1854 | ```csharp 1855 | using Battlehub.RTCommon; 1856 | using Battlehub.RTEditor.ViewModels; 1857 | 1858 | public class ExtendHierarchyWindow : RuntimeWindowExtension 1859 | { 1860 | public override string WindowTypeName => BuiltInWindowNames.Hierarchy; 1861 | 1862 | protected override void Extend(RuntimeWindow window) 1863 | { 1864 | ViewModelBase.ReplaceWith<HierarchyViewModelWithContextMenu>(window); 1865 | } 1866 | } 1867 | ``` 1868 | 1869 | ```csharp 1870 | using Battlehub.RTEditor.ViewModels; 1871 | using System.Collections.Generic; 1872 | using UnityEngine; 1873 | using UnityWeld.Binding; 1874 | 1875 | namespace Battlehub.RTEditor.Examples.Scene10 1876 | { 1877 | 1878 | [Binding] 1879 | public class HierarchyViewModelWithContextMenuExample : HierarchyViewModel 1880 | { 1881 | protected override void OnContextMenu(List<MenuItemViewModel> menuItems) 1882 | { 1883 | base.OnContextMenu(menuItems); 1884 | menuItems.Clear(); 1885 | 1886 | MenuItemViewModel myCommand = new MenuItemViewModel 1887 | { 1888 | Path = "My Context Menu Cmd", Command = "My Cmd Args" 1889 | }; 1890 | myCommand.Action = MenuCmd; 1891 | myCommand.Validate = ValidateMenuCmd; 1892 | menuItems.Add(myCommand); 1893 | } 1894 | 1895 | private void ValidateMenuCmd(MenuItemViewModel.ValidationArgs args) 1896 | { 1897 | if (!HasSelectedItems) 1898 | { 1899 | args.IsValid = false; 1900 | } 1901 | } 1902 | 1903 | private void MenuCmd(string arg) 1904 | { 1905 | Debug.Log($"My Context Menu Command with {arg}"); 1906 | } 1907 | } 1908 | } 1909 | ``` 1910 | 1911 | > **Note** 1912 | > You can combine the above approaches. 1913 | 1914 | > **Note** 1915 | > Most of the runtime editor windows follow the architecture described in the [window](#window) section. 1916 | 1917 | > **Note** 1918 | > For more examples, see **Scene10 - Extending Context Menu** in the [Example Scenes section](#example-scenes). 1919 | 1920 | ## Overriding the Default Layout 1921 | 1922 | To override the default layout, follow these steps: 1923 | 1924 | 1. **Create a Script Derived from LayoutExtension**: 1925 | Define a new script that inherits from `LayoutExtension`. 1926 | 1927 | 2. **Override GetLayoutInfo Method**: 1928 | Implement the `GetLayoutInfo` method to specify the custom layout. 1929 | 1930 | 3. **Add Script to GameObject**: 1931 | Create a new empty GameObject in your scene and attach the `ThreeColumnsLayoutExample` script to it. 1932 | 1933 | ### Example 1934 | 1935 | Here is an example script that creates a three-column layout (inspector, scene, hierarchy): 1936 | 1937 | ```csharp 1938 | using Battlehub.UIControls.DockPanels; 1939 | 1940 | namespace Battlehub.RTEditor.Examples.Layout 1941 | { 1942 | /// <summary> 1943 | /// Creates three columns layout (inspector, (scene, hierarchy)) 1944 | /// </summary> 1945 | public class ThreeColumnsLayoutExample : LayoutExtension 1946 | { 1947 | protected override LayoutInfo GetLayoutInfo(IWindowManager wm) 1948 | { 1949 | LayoutInfo scene = wm.CreateLayoutInfo(BuiltInWindowNames.Scene); 1950 | scene.IsHeaderVisible = true; 1951 | 1952 | LayoutInfo hierarchy = wm.CreateLayoutInfo(BuiltInWindowNames.Hierarchy); 1953 | LayoutInfo inspector = wm.CreateLayoutInfo(BuiltInWindowNames.Inspector); 1954 | 1955 | // Defines a region divided into two equal parts (ratio 1 / 2) 1956 | LayoutInfo sceneAndHierarchy = 1957 | LayoutInfo.Horizontal(scene, hierarchy, ratio: 1/2.0f); 1958 | 1959 | // Defines a region divided into two parts 1960 | // 1/3 for the inspector and 2/3 for the scene and hierarchy) 1961 | LayoutInfo layoutRoot = 1962 | LayoutInfo.Horizontal(inspector, sceneAndHierarchy, ratio: 1/3.0f); 1963 | 1964 | return layoutRoot; 1965 | } 1966 | } 1967 | } 1968 | ``` 1969 | 1970 | ![Add Three Columns Layout Example][override_layout_1] 1971 | 1972 | ![Result][override_layout_2] 1973 | 1974 | > **Note !!!** 1975 | > Make sure to remove conflicting layout extensions (like AutoSaveLayout). 1976 | 1977 | ![Remove Coflicting Layout Extensions][override_layout_3] 1978 | 1979 | > **Note** <br/> 1980 | For [examples](#example-scenes), see <br/> **Scene1 - Minimal**, <br/> **Scene2 - Minimal Mobile**, <br/> **Scene4 - Multiple Scenes**, <br/> **Scene5 - Layout SaveLoad** 1981 | 1982 | ### LayoutInfo Methods 1983 | 1984 | The `LayoutInfo` class provides three main methods to construct various types of layouts. These methods allow you to create complex layouts by splitting parent containers or grouping windows into tabs. Here's a description of each method: 1985 | 1986 | #### Vertical Split 1987 | 1988 | The `Vertical` method splits the parent container into two parts: one on top of the other. The specified ratio determines the size of the parts. A smaller ratio results in a smaller upper part. 1989 | 1990 | ```csharp 1991 | public static LayoutInfo Vertical(LayoutInfo top, LayoutInfo bottom, float ratio = 0.5f) 1992 | { 1993 | return new LayoutInfo(true, top, bottom, ratio); 1994 | } 1995 | ``` 1996 | 1997 | #### Horizontal Split 1998 | 1999 | The `Horizontal` method splits the parent container into two parts: one next to the other. The specified ratio determines the size of the parts. A smaller ratio results in a smaller left part. 2000 | 2001 | ```csharp 2002 | public static LayoutInfo Horizontal(LayoutInfo left, LayoutInfo right, float ratio = 0.5f) 2003 | { 2004 | return new LayoutInfo(false, left, right, ratio); 2005 | } 2006 | ``` 2007 | 2008 | #### Tab Group 2009 | 2010 | The `Group` method does not split the parent container. Instead, it creates a tab group, grouping multiple `LayoutInfo` instances into tabs within the same container. 2011 | 2012 | ```csharp 2013 | public static LayoutInfo Group(params LayoutInfo[] tabGroup) 2014 | { 2015 | return new LayoutInfo(tabGroup); 2016 | } 2017 | ``` 2018 | 2019 | #### Vertical Split Example 2020 | 2021 | Splitting a container into a scene view on top and a hierarchy view on the bottom with a 70% and 30% ratio respectively. 2022 | 2023 | ```csharp 2024 | LayoutInfo layout = LayoutInfo.Vertical(sceneViewLayout, hierarchyViewLayout, 0.7f); 2025 | ``` 2026 | 2027 | #### Horizontal Split Example 2028 | 2029 | Splitting a container into an inspector view on the left and a scene view on the right with a 30% and 70% ratio respectively. 2030 | 2031 | ```csharp 2032 | LayoutInfo layout = LayoutInfo.Horizontal(inspectorViewLayout, sceneViewLayout, 0.3f); 2033 | ``` 2034 | 2035 | #### Tab Group Example 2036 | 2037 | Grouping an inspector view and a console view into a single tab group. 2038 | 2039 | ```csharp 2040 | LayoutInfo layout = LayoutInfo.Group(inspectorViewLayout, consoleViewLayout); 2041 | ``` 2042 | 2043 | ## Overriding Scene Parameters 2044 | 2045 | 2046 | 1. **Create the Script**: Save the below code in a new C# script named `ScenesSetupExample.cs`. 2047 | 2. **Attach the Script**: Add the `ScenesSetupExample` script to a new GameObject in your scene. 2048 | 2049 | ### Example 2050 | 2051 | ```csharp 2052 | using Battlehub.RTCommon; 2053 | using Battlehub.RTHandles; 2054 | using UnityEngine; 2055 | 2056 | namespace Battlehub.RTEditor.Examples.SceneSetup 2057 | { 2058 | /// <summary> 2059 | /// This extension initializes 2D scene window 2060 | /// </summary> 2061 | public class SceneSetupExample : RuntimeWindowExtension 2062 | { 2063 | /// <summary> 2064 | /// Type of window to be extended 2065 | /// </summary> 2066 | public override string WindowTypeName => BuiltInWindowNames.Scene; 2067 | 2068 | protected override void Extend(RuntimeWindow window) 2069 | { 2070 | // Get a reference to the IRuntimeSceneComponent of the window 2071 | IRuntimeSceneComponent sceneComponent = 2072 | window.IOCContainer.Resolve<IRuntimeSceneComponent>(); 2073 | 2074 | // This is the point the camera looks at and orbits around 2075 | sceneComponent.Pivot = Vector3.zero; 2076 | 2077 | // Switch the scene component and scene camera to orthographic mode 2078 | sceneComponent.IsOrthographic = true; 2079 | 2080 | // Disable scene gizmo 2081 | sceneComponent.IsSceneGizmoEnabled = false; 2082 | 2083 | // Disable rotation 2084 | sceneComponent.CanRotate = false; 2085 | 2086 | // Disable free move 2087 | sceneComponent.CanFreeMove = false; 2088 | 2089 | // Prevent camera position changes when zooming in and out 2090 | sceneComponent.ChangeOrthographicSizeOnly = true; 2091 | 2092 | // Set initial orthographic size 2093 | sceneComponent.OrthographicSize = 5.0f; 2094 | 2095 | // Set camera position according to window.Args 2096 | const float distance = 100; 2097 | sceneComponent.CameraPosition = -Vector3.forward * distance; 2098 | } 2099 | } 2100 | } 2101 | ``` 2102 | 2103 | ### Explanation 2104 | 2105 | - **WindowTypeName**: Specifies the type of window to be extended. In this case, it is set to `BuiltInWindowNames.Scene`. 2106 | - **Extend Method**: Contains the logic to configure the scene window. It sets various properties of the `IRuntimeSceneComponent` 2107 | to customize the scene view, such as setting the camera to orthographic mode, disabling certain features, and setting the initial camera position and size. 2108 | 2109 | ![Override Scene Params][override_scene_params] 2110 | 2111 | ## Overriding Tools Panel 2112 | 2113 | To override the tools panel in the runtime editor, follow these steps: 2114 | 2115 | 1. Create a script named `ToolsPanelOverride`. 2116 | 2. Create a new GameObject and add the `ToolsPanelOverride` component to it. 2117 | 3. Set the `Tools Prefab` field in the Inspector. 2118 | 2119 | Here's an example script demonstrating how to override the tools panel: 2120 | 2121 | ```csharp 2122 | using Battlehub.RTEditor; 2123 | using UnityEngine; 2124 | 2125 | public class ToolsPanelOverride : LayoutExtension 2126 | { 2127 | [SerializeField] 2128 | private Transform m_toolsPrefab = null; 2129 | 2130 | protected override void OnBeforeBuildLayout(IWindowManager wm) 2131 | { 2132 | wm.OverrideTools(m_toolsPrefab); 2133 | } 2134 | } 2135 | ``` 2136 | 2137 | ![Override Tools][override_tools] 2138 | 2139 | > **Note** </br> 2140 | The original tools view can be found in: Assets/Battlehub/RTEditor/Content/Runtime/RTEditor/Prefabs/Views/**ToolsView.prefab** 2141 | 2142 | ## Setting ui scale 2143 | ## Overriding UI Scale 2144 | 2145 | To override the UI scale in the runtime editor, follow these steps: 2146 | 2147 | 1. Create a script named `UIScaleOverride`. 2148 | 2. Create a new GameObject and add the `UIScaleOverride` component to it. 2149 | 3. Set the desired scale in the Inspector. 2150 | 2151 | ### Example Script 2152 | 2153 | Here's an example script demonstrating how to override the UI scale: 2154 | 2155 | ```csharp 2156 | using Battlehub.RTCommon; 2157 | using Battlehub.RTEditor; 2158 | using UnityEngine; 2159 | 2160 | public class UIScaleOverride : LayoutExtension 2161 | { 2162 | [SerializeField] 2163 | private float Scale = 2; 2164 | 2165 | protected override void OnInit() 2166 | { 2167 | ISettingsComponent settings = IOC.Resolve<ISettingsComponent>(); 2168 | settings.UIScale = Scale; 2169 | } 2170 | } 2171 | ``` 2172 | 2173 | #### Before UI Scale Override 2174 | ![Before UI Scale Override][ui_scale_1] 2175 | 2176 | #### After UI Scale Override 2177 | ![After UI Scale Override][ui_scale_2] 2178 | 2179 | ## Overriding the Theme 2180 | 2181 | To override the theme in the runtime editor, follow these steps: 2182 | 2183 | 1. Create a script named `OverrideTheme`. 2184 | 2. Create a new GameObject and add the `OverrideTheme` component to it. 2185 | 3. Assign a ThemeAsset to the `m_theme` field in the Inspector. 2186 | 2187 | ### Example Script 2188 | 2189 | Here's an example script demonstrating how to override the theme: 2190 | 2191 | ```csharp 2192 | using Battlehub.RTCommon; 2193 | using UnityEngine; 2194 | 2195 | namespace Battlehub.RTEditor.Demo 2196 | { 2197 | public class OverrideTheme : EditorExtension 2198 | { 2199 | [SerializeField] 2200 | private ThemeAsset m_theme; 2201 | 2202 | protected override void OnInit() 2203 | { 2204 | ISettingsComponent settings = IOC.Resolve<ISettingsComponent>(); 2205 | settings.SelectedTheme = m_theme; 2206 | } 2207 | } 2208 | } 2209 | ``` 2210 | ![Override Theme][override_theme] 2211 | 2212 | ## Inspector View 2213 | The main purpose of the inspector is to create different editors depending on the type of selected object and its components. Here is a general idea of what is happening: 2214 | 2215 | 1. **GameObject Selection**: When the user selects a GameObject, the inspector creates a GameObject editor. 2216 | 2. **Component Editors Creation**: The GameObject editor creates component editors for each component attached to the selected GameObject. 2217 | 3. **Property Editors Creation**: Each component editor creates property editors for the properties of the component. 2218 | 2219 | ![Insepector][inspector] 2220 | 2221 | ### Inspector Editors 2222 | 2223 | In the `Assets/Battlehub/RTEditor/Content/Runtime/RTEditor/Prefabs/Editors` folder, you can find various editor prefabs like `GameObjectEditor`, `AssetEditor`, `LayerEditor`, and `MaterialEditor`. 2224 | These editors are used by the inspector to create specific editing ui based on the selected object's type and its components. 2225 | ![Insepector][inspector_editors] 2226 | 2227 | ### Property Editors 2228 | 2229 | Property editors, created by component editors, are located in the `Assets/Battlehub/RTEditor/Content/Runtime/RTEditor/Prefabs/Editors/PropertyEditors` folder. 2230 | These editors provide specific ui for editing individual properties of components within the inspector. 2231 | ![Inspector][inspector_property_editors] 2232 | 2233 | ## Inspector Configuration 2234 | 2235 | To configure the editors used by the inspector, follow these steps: 2236 | 2237 | 1. Click `Tools -> Runtime Editor -> Inspector Configuration`. 2238 | 2239 | ![Inspector Configuration][inspector_configuration_1] 2240 | 2241 | 2. The configuration window has five sections: 2242 | - **Object Editors**: Select which editor to use for Game Objects and Asset Editors. 2243 | - **Property Editors**: Select which editors to use for component properties. 2244 | - **Material Editors**: Select which editors to use for materials. 2245 | - **Standard Component Editors**: Select which editors to use for standard components. 2246 | - **Script Editors**: Select which editors to use for scripts. 2247 | 2248 | 3. After selecting and enabling the desired component editors, click the `Save Editors Map` button. 2249 | 2250 | ![Save Editors Map][inspector_configuration_window] 2251 | 2252 | ### Register Editors Programatically 2253 | 2254 | To register property editors programmatically, you need to create a script that defines and registers your custom property editors. Below is an example demonstrating how to achieve this: 2255 | 2256 | ```csharp 2257 | using Battlehub.RTCommon; 2258 | using Battlehub.RTEditor; 2259 | using UnityEngine; 2260 | 2261 | public class RegisterPropertyEditorsExample : EditorExtension 2262 | { 2263 | [SerializeField] 2264 | private GameObject m_vector3Editor = null; 2265 | 2266 | [SerializeField] 2267 | private GameObject m_vector2Editor = null; 2268 | 2269 | protected override void OnInit() 2270 | { 2271 | base.OnInit(); 2272 | 2273 | IEditorsMap editorsMap = IOC.Resolve<IEditorsMap>(); 2274 | if (m_vector3Editor != null) 2275 | { 2276 | editorsMap.RemoveMapping(typeof(Vector3)); 2277 | editorsMap.AddMapping(typeof(Vector3), m_vector3Editor, true, true); 2278 | EnableStyling(m_vector3Editor); 2279 | } 2280 | 2281 | if (m_vector2Editor != null) 2282 | { 2283 | editorsMap.RemoveMapping(typeof(Vector2)); 2284 | editorsMap.AddMapping(typeof(Vector2), m_vector2Editor, true, true); 2285 | EnableStyling(m_vector2Editor); 2286 | } 2287 | } 2288 | } 2289 | ``` 2290 | 2291 | ### Steps to Register Component Editors Programmatically 2292 | 2293 | 1. **Create a New Script**: Create a new C# script, for example, `RegisterPropertyEditorsExample.cs`. 2294 | 2295 | 2. **Implement the Script**: Implement the script as shown above, registering your custom property editors. 2296 | 2297 | 3. **Attach the Script to a GameObject**: Create a new GameObject in your scene and attach the `RegisterPropertyEditorsExample` script to it 2298 | 2299 | To register a custom component editor programmatically, you can use a similar approach to the one used for registering property editors. Below is an example demonstrating how to achieve this: 2300 | 2301 | ```csharp 2302 | using Battlehub.RTCommon; 2303 | using Battlehub.RTEditor; 2304 | using UnityEngine; 2305 | 2306 | public class CustomComponentEditorInit : EditorExtension 2307 | { 2308 | [SerializeField] 2309 | private GameObject m_terrainComponentEditor = null; 2310 | 2311 | protected override void OnInit() 2312 | { 2313 | base.OnInit(); 2314 | 2315 | IEditorsMap editorsMap = IOC.Resolve<IEditorsMap>(); 2316 | if (m_terrainComponentEditor != null) 2317 | { 2318 | if (!editorsMap.HasMapping(typeof(Terrain))) 2319 | { 2320 | editorsMap.AddMapping(typeof(Terrain), 2321 | m_terrainComponentEditor, enabled: true, isPropertyEditor: false); 2322 | } 2323 | } 2324 | } 2325 | } 2326 | ``` 2327 | 2328 | ### Component Properties Visibility 2329 | 2330 | To select the properties displayed by the component editor, you need to create a class that inherits from `ComponentDescriptorBase<>`. Implement the `GetProperties` method to return `PropertyDescriptors` for all properties that will be present in the component editor UI. 2331 | 2332 | For example, suppose you have the following component, and you want to display `Field1`, `Property1`, and a button that will call `Method()`. `Field2` and `Property2` must stay hidden. 2333 | 2334 | ```csharp 2335 | public class MyComponent : MonoBehaviour 2336 | { 2337 | public string Field1; 2338 | 2339 | public string Field2; 2340 | 2341 | public string Property1 2342 | { 2343 | get; 2344 | set; 2345 | } 2346 | 2347 | public string Property2 2348 | { 2349 | get; 2350 | set; 2351 | } 2352 | 2353 | public void Method() 2354 | { 2355 | Debug.Log("Method"); 2356 | } 2357 | } 2358 | ``` 2359 | 2360 | To achieve this, create the following component descriptor: 2361 | 2362 | ```csharp 2363 | using Battlehub.RTEditor; 2364 | using Battlehub.Utils; 2365 | using UnityEngine; 2366 | 2367 | public class MyComponentDescriptor : ComponentDescriptorBase<MyComponent> 2368 | { 2369 | public override PropertyDescriptor[] GetProperties(ComponentEditor editor, object converter) 2370 | { 2371 | var field1 = Strong.MemberInfo((MyComponent x) => x.Field1); 2372 | var property1 = Strong.MemberInfo((MyComponent x) => x.Property1); 2373 | var method = Strong.MethodInfo((MyComponent x) => x.Method()); 2374 | 2375 | return new[] 2376 | { 2377 | new PropertyDescriptor("My Field", editor.Components, field1), 2378 | new PropertyDescriptor("My Property", editor.Components, property1), 2379 | new PropertyDescriptor("Click Me!", editor.Components, method), 2380 | }; 2381 | } 2382 | } 2383 | ``` 2384 | 2385 | And enable the ComponentEditor using the [Inspector Configuration Window](#inspector-configuration). 2386 | 2387 | ![My Component][my_component] 2388 | 2389 | Here is more complex example of a `TransformComponentDescriptor`: 2390 | 2391 | ```csharp 2392 | using UnityEngine; 2393 | using System.Reflection; 2394 | using Battlehub.Utils; 2395 | using Battlehub.RTCommon; 2396 | 2397 | namespace Battlehub.RTEditor 2398 | { 2399 | public class TransformComponentDescriptor : ComponentDescriptorBase<Transform> 2400 | { 2401 | public override object CreateConverter(ComponentEditor editor) 2402 | { 2403 | object[] converters = new object[editor.Components.Length]; 2404 | Component[] components = editor.Components; 2405 | for (int i = 0; i < components.Length; ++i) 2406 | { 2407 | Transform transform = (Transform)components[i]; 2408 | if (transform != null) 2409 | { 2410 | converters[i] = new TransformPropertyConverter 2411 | { 2412 | ExposeToEditor = transform.GetComponent<ExposeToEditor>() 2413 | }; 2414 | } 2415 | } 2416 | return converters; 2417 | } 2418 | 2419 | public override PropertyDescriptor[] GetProperties( 2420 | ComponentEditor editor, object converter) 2421 | { 2422 | object[] converters = (object[])converter; 2423 | 2424 | MemberInfo position = Strong.PropertyInfo( 2425 | (Transform x) => x.localPosition, "localPosition"); 2426 | MemberInfo positionConverted = Strong.PropertyInfo( 2427 | (TransformPropertyConverter x) => x.LocalPosition, "LocalPosition"); 2428 | MemberInfo rotation = Strong.PropertyInfo( 2429 | (Transform x) => x.localRotation, "localRotation"); 2430 | MemberInfo rotationConverted = Strong.PropertyInfo( 2431 | (TransformPropertyConverter x) => x.LocalEuler, "LocalEulerAngles"); 2432 | MemberInfo scale = Strong.PropertyInfo( 2433 | (Transform x) => x.localScale, "localScale"); 2434 | MemberInfo scaleConverted = Strong.PropertyInfo( 2435 | (TransformPropertyConverter x) => x.LocalScale, "LocalScale"); 2436 | 2437 | return new[] 2438 | { 2439 | new PropertyDescriptor("Position", converters, positionConverted, position), 2440 | new PropertyDescriptor("Rotation", converters, rotationConverted, rotation), 2441 | new PropertyDescriptor("Scale", converters, scaleConverted, scale) 2442 | }; 2443 | } 2444 | } 2445 | } 2446 | 2447 | ``` 2448 | 2449 | > **Note** </br> 2450 | TransformPropertyConverter is used to convert a quaternion to Euler angles, enabling the use of Vector3Editor instead of QuaternionEditor. 2451 | 2452 | > **Note** <br/> 2453 | The remaining built-in component descriptors can be found in the `Assets/Battlehub/RTEditor/Runtime/RTEditor/Editors/ComponentDescriptors` folder. 2454 | 2455 | > **Note** </br> 2456 | To save a new component, you should create a **Surrogate** class for it. See the [Serializer Extensions](#serializer-extensions) section for details. 2457 | 2458 | ### Custom Type Properties Visibility 2459 | 2460 | Here is the example on how to create custom type property descriptor: 2461 | 2462 | ```csharp 2463 | using System.Collections.Generic; 2464 | using Battlehub.RTEditor; 2465 | using UnityEngine; 2466 | 2467 | 2468 | public class CustomTypeDescriptor : CustomTypeDescriptorBase<CustomType> 2469 | { 2470 | public override PropertyDescriptor[] GetProperties() 2471 | { 2472 | return new [] 2473 | { 2474 | Property("Name", (CustomType x) => x.Name), 2475 | Property("Val", (CustomType x) => x.Value, new Range(1, 100)), 2476 | Property("Col", (CustomType x) => x.Color), 2477 | Method("Reset", (CustomType x) => x.Reset()) 2478 | }; 2479 | } 2480 | } 2481 | 2482 | [System.Serializable] 2483 | public class CustomType 2484 | { 2485 | [SerializeField] 2486 | private string m_name; 2487 | 2488 | public string Name 2489 | { 2490 | get { return m_name; } 2491 | set { m_name = value; } 2492 | } 2493 | 2494 | [SerializeField] 2495 | private float m_value; 2496 | 2497 | public float Value 2498 | { 2499 | get { return m_value; } 2500 | set { m_value = value; } 2501 | } 2502 | 2503 | [SerializeField] 2504 | private Color m_color; 2505 | 2506 | public Color Color 2507 | { 2508 | get { return m_color; } 2509 | set { m_color = value; } 2510 | } 2511 | 2512 | public void Reset() 2513 | { 2514 | Color = Color.white; 2515 | Value = 0; 2516 | } 2517 | } 2518 | 2519 | public class NewBehaviourScript : MonoBehaviour 2520 | { 2521 | [SerializeField] 2522 | private CustomType m_field = new CustomType(); 2523 | 2524 | [SerializeField] 2525 | private List<CustomType> m_list; 2526 | } 2527 | ``` 2528 | 2529 | ### Customizing Component Editor Header 2530 | 2531 | To customize the header of a component editor, you may want to override the `GetHeaderDescriptor` method. This allows you to specify various aspects of the header, such as its display name, the visibility of certain buttons, and whether to show an icon. 2532 | 2533 | Here is an example of how to override the `GetHeaderDescriptor` method in a component descriptor: 2534 | 2535 | ```csharp 2536 | public class MyComponentDescriptor : ComponentDescriptorBase<MyComponent> 2537 | { 2538 | public override HeaderDescriptor GetHeaderDescriptor(IRTE editor) 2539 | { 2540 | // Customize the header descriptor as needed 2541 | return new HeaderDescriptor( 2542 | displayName: "My Custom Component", 2543 | showExpander: true, 2544 | showResetButton: true, 2545 | showEnableButton: true, 2546 | showRemoveButton: true, 2547 | showIcon: true, 2548 | icon: null 2549 | ); 2550 | } 2551 | } 2552 | ``` 2553 | 2554 | ### HeaderDescriptor Structure 2555 | 2556 | The `HeaderDescriptor` struct allows you to configure the appearance and functionality of the component editor's header. It has the following properties: 2557 | 2558 | - **DisplayName**: The display name of the component. 2559 | - **ShowExpander**: Determines whether to show the expander toggle (usually used to collapse or expand the component's properties). 2560 | - **ShowResetButton**: Determines whether to show the reset button. 2561 | - **ShowEnableButton**: Determines whether to show the enable/disable button. 2562 | - **ShowRemoveButton**: Determines whether to show the remove button. 2563 | - **Icon**: The icon to be displayed in the header. 2564 | - **ShowIcon**: Determines whether to show the icon. 2565 | 2566 | ## Localization 2567 | 2568 | To localize your application, follow these steps to create and load string resources. 2569 | 2570 | 1. Create a file named `My.StringResources.en-US.xml` with the following format and place it in the `Resources` folder: 2571 | 2572 | ```xml 2573 | <?xml version="1.0" encoding="utf-8"?> 2574 | <StringResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 2575 | <Resources> 2576 | <!-- Example String Resource --> 2577 | <StringResource id="ID_String" value="Localized String"/> 2578 | </Resources> 2579 | </StringResources> 2580 | ``` 2581 | 2582 | 2. Load the string resources using the following code: 2583 | 2584 | ```csharp 2585 | ILocalization lc = IOC.Resolve<ILocalization>(); 2586 | lc.LoadStringResources("My.StringResources"); 2587 | ``` 2588 | 2589 | 3. Retrieve localized strings using the `GetString` method: 2590 | 2591 | ```csharp 2592 | var localizedString = lc.GetString("ID_String"); 2593 | ``` 2594 | 2595 | ### Built-in String Resources 2596 | 2597 | Runtime Editor includes the following built-in string resources: 2598 | - `RTBuilder.StringResources.en-US` 2599 | - `RTDeformer.StringResources.en-US` 2600 | - `RTEditor.StringResources.en-US` 2601 | - `RTTerrain.StringResources.en-US` 2602 | 2603 | > **Note** </br> 2604 | Locale can be changed using following code `lc.Locale = "en-US";` This will work provided that string resources files with the corresponding prefix exist. 2605 | 2606 | # UI Controls 2607 | ## Dock Panel Control 2608 | https://rteditor.battlehub.net/v350/manual/dock-panels.html 2609 | ## Tree View Control 2610 | https://rteditor.battlehub.net/v350/manual/vtv.html 2611 | ## Menu Control 2612 | https://rteditor.battlehub.net/v350/manual/menu-control.html 2613 | 2614 | 2615 | # Runtime Transform Handles 2616 | 2617 | Runtime Transform Handles are the runtime 3D controls used to manipulate GameObjects in the scene. 2618 | There are three built-in transform tools for positioning, rotating, and scaling objects via the transform component. 2619 | Supplementary controls, such as the scene gizmo and grid, allow for changing the viewing angle and projection mode, and for showing the grid in the scene. 2620 | Other important components include 2621 | 2622 | - **Selection Component**: Allows for selecting objects. 2623 | - **Scene Component**: Facilitates navigation within the scene. 2624 | - **Handles Component**: Enables changing the appearance of transform handles. 2625 | 2626 | https://github.com/BattlehubCode/RTH_Docs 2627 | 2628 | ## Runtime Gizmos 2629 | 2630 | Runtime Gizmos are the runtime 3D controls used to manipulate items in the scene. Unlike transform handles, gizmos do not modify the transformation of objects. 2631 | Instead, they are used to modify colliders, bounding boxes, and properties of light and audio sources. 2632 | 2633 | https://rteditor.battlehub.net/v350/manual/gizmos.html 2634 | 2635 | # Animation Editor 2636 | 2637 | https://rteditor.battlehub.net/v350/manual/animation-editor.html 2638 | 2639 | # Runtime Editor Extensions 2640 | 2641 | https://rteditor.battlehub.net/v350/manual/editor-extensions.html 2642 | 2643 | # Runtime Scripting 2644 | 2645 | In Runtime Editor 4.4.0, support for Jint as a runtime scripting backend was added. 2646 | Jint is a JavaScript interpreter for .NET that can run on any modern .NET platform, as it supports .NET Standard 2.0 and .NET 4.6.2 targets (and later). [Learn more about Jint here](https://github.com/sebastienros/jint). 2647 | 2648 | ## Getting Started with Runtime Scripting 2649 | 2650 | 1. Unpack `Assets/Battlehub/3 RTEditor Scripting (Jint).unitypackage`. 2651 | 2. If you're starting with a new scene, click **Tools > Runtime Editor > Create RTEditor**. 2652 | 3. Click **Tools > Runtime Editor > Add Runtime Scripting (Jint)**. 2653 | ![Add Jint Scripting][jint_scripting_1] 2654 | 4. Start the Runtime Editor. 2655 | 5. In the Project View, right-click and select **Create > JintScript** from the context menu. 2656 | ![Create Jint Script][jint_scripting_2] 2657 | 6. Create an empty GameObject. 2658 | ![Create Empty GameObject][jint_scripting_3] 2659 | 7. In the Inspector, click **Add Component** and type `JintScript`. 2660 | ![Add Component][jint_scripting_4] 2661 | 8. Click **Play** in the Runtime Editor. 2662 | 9. Observe `Debug.Log` messages printed by `JintScript` in the console. 2663 | ![Click Play in Runtime Editor][jint_scripting_5] 2664 | 10. You can open the script editor by double-clicking the script in the Project View. 2665 | ![Script Editor][jint_scripting_6] 2666 | 2667 | ## Jint Script 2668 | A typical Jint script looks as follows: 2669 | 2670 | ```javascript 2671 | var UnityEngine = importNamespace('UnityEngine'); 2672 | 2673 | var Awake = function (component) { 2674 | UnityEngine.Debug.Log("Awake"); 2675 | }; 2676 | 2677 | var Start = function (component) { 2678 | UnityEngine.Debug.Log("Start"); 2679 | }; 2680 | 2681 | var OnDestroy = function (component) { 2682 | UnityEngine.Debug.Log("OnDestroy"); 2683 | }; 2684 | 2685 | return { 2686 | Awake: Awake, 2687 | Start: Start, 2688 | OnDestroy: OnDestroy 2689 | }; 2690 | ``` 2691 | 2692 | Method names correspond to the names of Unity MonoBehaviour methods. You can declare Awake, OnEnable, Start, OnDisable, Update, FixedUpdate, and OnDestroy. 2693 | There are two additional methods called by the Runtime Editor: OnRemove and OnRestore. The Runtime Editor does not immediately delete GameObjects; they remain in the undo stack until eventually destroyed, at which point the OnDestroy method is called. If you restore a GameObject from the undo/redo stack, the OnRestore method will be called. 2694 | Other methods like LateUpdate and OnApplicationQuit are currently not supported. 2695 | 2696 | ## Declaring variables 2697 | There are four types of properties that can be exposed to the Runtime Editor and displayed in the Inspector: String, Number, Boolean, and Object. 2698 | 2699 | ```javascript 2700 | var UnityEngine = importNamespace('UnityEngine'); 2701 | var Log = UnityEngine.Debug.Log; 2702 | 2703 | var Start = function(component) { 2704 | Log(this.String + "", "" + this.Number + "", "" + this.Boolean + "", "" + this.Object); 2705 | } 2706 | 2707 | return { 2708 | String: ""Runtime Editor"", 2709 | Number: 2024, 2710 | Boolean: true, 2711 | Object: {}, 2712 | 2713 | Start : Start 2714 | }; 2715 | ``` 2716 | ![Properties][jint_scripting_7] 2717 | 2718 | ## Using UnityEngine API 2719 | 2720 | Using the Unity API from a Jint script is intuitive and not much different from how it's used in C#. 2721 | However, to access methods of certain objects, you need to unwrap the object using the u(obj) method, which is a shortcut for jint.clrHelper.unwrap(obj). 2722 | See the u(rb) call in the example below and refer to the following issue for details: [https://github.com/sebastienros/jint/pull/1613](https://github.com/sebastienros/jint/pull/1613) 2723 | 2724 | ``` javascript 2725 | var UnityEngine = importNamespace('UnityEngine'); 2726 | var RTCommon = importNamespace('Battlehub.RTCommon'); 2727 | 2728 | var GameObject = UnityEngine.GameObject; 2729 | var PrimitiveType = UnityEngine.PrimitiveType; 2730 | var Rigidbody = UnityEngine.Rigidbody; 2731 | var Vector3 = UnityEngine.Vector3; 2732 | 2733 | var ExposeToEditor = RTCommon.ExposeToEditor; 2734 | 2735 | var Start = function (component) { 2736 | var plane = GameObject.CreatePrimitive(PrimitiveType.Plane); 2737 | plane.transform.parent = component.transform.parent; 2738 | plane.position = Vector3.zero; 2739 | plane.AddComponent(ExposeToEditor); 2740 | 2741 | var go = GameObject.CreatePrimitive(PrimitiveType.Cube); 2742 | go.transform.parent = component.transform.parent; 2743 | go.transform.position = new Vector3(0, 10, 0); 2744 | go.AddComponent(ExposeToEditor); 2745 | 2746 | var rb = go.AddComponent(Rigidbody); 2747 | u(rb).AddForce( 2748 | Vector3.right, 2749 | UnityEngine.ForceMode.Impulse); 2750 | } 2751 | 2752 | return { 2753 | Start: Start 2754 | }; 2755 | " 2756 | ``` 2757 | ![Properties][jint_scripting_8] 2758 | 2759 | ## JintComponent.Add, JintComponent.Get 2760 | 2761 | JintComponent.Add can be used to add a Jint script by name, equivalent to AddComponent. 2762 | 2763 | ``` javascript 2764 | var UnityEngine = importNamespace('UnityEngine'); 2765 | var RTScripting = importNamespace('Battlehub.RTScripting'); 2766 | 2767 | var Hello = function (arg) { 2768 | UnityEngine.Debug.Log(""Hi "" + arg + "", nice to meet you!"") 2769 | } 2770 | 2771 | var Start = function (component) { 2772 | RTScripting.JintComponent.Add(component.gameObject, ""TestJintComponent"") 2773 | } 2774 | 2775 | return { 2776 | Start: Start, 2777 | Hello: Hello 2778 | }; 2779 | ``` 2780 | 2781 | JintComponent.Get can be used to retrieve a Jint script by name, equivalent to GetComponent. 2782 | 2783 | ```javascript 2784 | var UnityEngine = importNamespace('UnityEngine'); 2785 | var RTScripting = importNamespace('Battlehub.RTScripting'); 2786 | 2787 | var Start = function (component) { 2788 | UnityEngine.Debug.Log(""I am Test Component!""); 2789 | 2790 | var otherComponent = RTScripting.JintComponent.Get(component.gameObject, ""AddJintComponent""); 2791 | otherComponent.Call(""Hello"", ""Test Component""); 2792 | } 2793 | 2794 | return { 2795 | Start: Start 2796 | } 2797 | ``` 2798 | 2799 | ## Create Jint Script programmatically 2800 | 2801 | The following approach can be used to create Jint scripts programmatically: 2802 | 2803 | ```csharp 2804 | private async Task CreateExampleScriptAsync(string gameObjectName, string scriptName, string scriptCode) 2805 | { 2806 | var editor = IOC.Resolve<IRuntimeEditor>(); 2807 | 2808 | // Get script path inside root folder 2809 | var scriptPath = editor.GetRootFolderPath($"{scriptName}.js"); 2810 | 2811 | // Create script asset 2812 | await editor.CreateAssetAsync(scriptCode, scriptPath, forceOverwrite: true); 2813 | 2814 | // Create game object 2815 | var go = CreateGameObject(gameObjectName); 2816 | 2817 | // And add it to the Scene 2818 | editor.AddGameObjectToScene(go); 2819 | 2820 | // Add JintComponent with newly created script 2821 | go.AddJintComponent(scriptName); 2822 | } 2823 | ``` 2824 | 2825 | ```csharp 2826 | string scriptCode = @" 2827 | var UnityEngine = importNamespace('UnityEngine'); 2828 | 2829 | UnityEngine.Debug.Log(""Hello World!""); 2830 | "; 2831 | 2832 | CreateExampleScript( 2833 | gameObjectName: "Hello World", 2834 | scriptName: "Hello World", 2835 | scriptCode: scriptCode); 2836 | ``` 2837 | 2838 | ## More Runtime Scripting samples 2839 | 2840 | 1. Unpack **Assets/Battlehub/3 RTEditor Scripting Demo (Jint).unitypackage.** 2841 | 2. Click Tools > Runtime Editor > Show me examples. 2842 | 3. Open **Scene61 - Scripting (Jint)** in the [Example Scenes section](#example-scenes). 2843 | ![Properties][jint_scripting_9] 2844 | 2845 | # Asset Database 2846 | In **Runtime Editor 4.0.0**, the RTSL subsystem was replaced by the [Runtime Asset Database](https://github.com/Battlehub0x/RuntimeAssetDatabase). 2847 | For information on making the runtime editor compatible with projects created using previous versions, see the [Compatibility Modes](#compatibility-modes) section. 2848 | The documentation for Runtime Save Load can be found [here](https://rteditor.battlehub.net/v350/manual/save-load.html). 2849 | This section focuses on the new asset database and new API methods. 2850 | 2851 | ## Core Methods and Events 2852 | 2853 | Most of the new core project, scene, prefab, and asset management methods are defined in the `IRuntimeEditor` and `IAssetDatabaseModel` interfaces. 2854 | 2855 | ## IRuntimeEditor and IAssetDatabaseModel Interfaces 2856 | 2857 | ### IRuntimeEditor Interface 2858 | 2859 | The `IRuntimeEditor` interface extends `IRTE` and `IAssetDatabaseModel` to provide a comprehensive set of methods and properties for managing projects, scenes, and assets in the Runtime Editor. Key functionalities include: 2860 | 2861 | - **Project Management**: 2862 | - Create, load, delete, and list projects. 2863 | - Check if a project type is supported. 2864 | - Handle project-related events. 2865 | 2866 | - **Scene Management**: 2867 | - Create new scenes and save scenes with specified parameters. 2868 | - Handle scene-related events. 2869 | 2870 | - **Asset Management**: 2871 | - Create assets from binary data, text, or Unity objects. 2872 | - Handle asset-related events. 2873 | 2874 | - **Window Management**: 2875 | - Create or activate editor windows. 2876 | - Reset to default layout. 2877 | 2878 | ### IAssetDatabaseModel Interface 2879 | 2880 | The `IAssetDatabaseModel` interface provides methods and events for managing assets within the runtime editor. It includes: 2881 | 2882 | - **Event Handling**: 2883 | - Events for project loading, unloading, asset creation, deletion, and modification. 2884 | - Events for scene initialization, asset instantiation, and changes in asset selection. 2885 | 2886 | - **Asset Operations**: 2887 | - Methods to load, save, and delete assets. 2888 | - Methods to move, duplicate, and instantiate assets. 2889 | - Methods to open, close, and edit assets and prefabs. 2890 | - Methods to manage asset folders. 2891 | 2892 | - **Utility Methods**: 2893 | - Methods to add and remove runtime serializable types. 2894 | - Methods to add and remove extensions and external asset loaders. 2895 | - Methods to check various conditions related to assets, instances, and prefabs. 2896 | 2897 | ### Key Methods and Properties 2898 | 2899 | #### IRuntimeEditor 2900 | ```csharp 2901 | Task<ProjectListEntry[]> GetProjectsAsync(); 2902 | Task<ProjectListEntry> CreateProjectAsync(string projectPath); 2903 | Task<ProjectListEntry> DeleteProjectAsync(string projectPath); 2904 | void NewScene(bool confirm = true); 2905 | void SaveScene(); 2906 | void SaveSceneAs(); 2907 | Task SaveCurrentSceneAsync(ID folderID, string name); 2908 | Task SaveCurrentSceneAsync(string path = null); 2909 | Task CreateAssetAsync(byte[] binaryData, string path, bool forceOverwrite = false, bool select = true); 2910 | Task CreateAssetAsync(string text, string path, bool forceOverwrite = false, bool select = true); 2911 | Task CreateAssetAsync(UnityObject obj, string path = null, bool forceOverwrite = false, bool? includeDependencies = null, bool? variant = null, bool select = true); 2912 | ``` 2913 | 2914 | #### IAssetDatabaseModel 2915 | ```csharp 2916 | Task LoadProjectAsync(string projectID, string version = null); 2917 | Task UnloadProjectAsync(); 2918 | Task<ID> CreateFolderAsync(string path); 2919 | Task<ID> CreateAssetAsync(object obj, string path, bool variant = false, bool extractSubassets = false); 2920 | Task<ID> ImportExternalAssetAsync(ID folderID, object key, string loaderID, string desiredName); 2921 | Task<ID> ImportExternalAssetAsync(ID folderID, ID assetID, object key, string loaderID, string desiredName); 2922 | Task InitializeNewSceneAsync(); 2923 | Task UnloadAllAndClearSceneAsync(); 2924 | Task SaveAssetAsync(ID assetID); 2925 | Task UpdateThumbnailAsync(ID assetID); 2926 | Task MoveAssetsAsync(IReadOnlyList<ID> assetIDs, IReadOnlyList<string> toPaths); 2927 | Task DuplicateAssetsAsync(IReadOnlyList<ID> assetIDs, IReadOnlyList<string> toPaths); 2928 | Task DeleteAssetsAsync(IReadOnlyList<ID> assetIDs); 2929 | Task SelectPrefabAsync(GameObject instance); 2930 | Task OpenPrefabAsync(GameObject instance); 2931 | Task ClosePrefabAsync(); 2932 | Task OpenAssetAsync(ID assetID); 2933 | Task<InstantiateAssetsResult> InstantiateAssetsAsync(ID[] assetIDs, Transform parent = null); 2934 | Task DetachAsync(GameObject[] instances, bool completely); 2935 | Task SetDirtyAsync(Component component); 2936 | Task DuplicateAsync(GameObject[] instances); 2937 | Task ReleaseAsync(GameObject[] instances); 2938 | Task ApplyChangesAsync(GameObject instance); 2939 | Task ApplyToBaseAsync(GameObject instance); 2940 | Task RevertToBaseAsync(GameObject instance); 2941 | Task<byte[]> SerializeAsync(object asset); 2942 | Task<object> DeserializeAsync(byte[] data, object target = null); 2943 | Task<T> GetValueAsync<T>(string key); 2944 | Task SetValueAsync<T>(string key, T obj); 2945 | Task DeleteValueAsync<T>(string key); 2946 | ``` 2947 | 2948 | ## IAssetDatabaseModel vs IRuntimeEditor Interface 2949 | 2950 | ```csharp 2951 | var assetDatabase = IOC.Resolve<IAssetDatabaseModel>(); 2952 | var runtimeEditor = IOC.Resolve<IRuntimeEditor>(); 2953 | ``` 2954 | 2955 | These two interfaces can be used almost interchangeably. 2956 | 2957 | `IAssetDatabaseModel` has fewer methods, but it might be a good idea to use it if you want some parts of your code to run without opening the runtime editor. 2958 | 2959 | Apart from the runtime editor itself, there are two implementations of the `IAssetDatabaseModel` interface: `AssetDatabaseModel` and `AssetDatabaseModelOverRTSL` (which exists for [compatibility](#compatibility-modes) with projects created using the Runtime Save Load subsystem in previous runtime editor versions). 2960 | 2961 | - The `AssetDatabaseModel` is a wrapper for the [Runtime Asset Database](https://github.com/Battlehub0x/RuntimeAssetDatabase). 2962 | - The `AssetDatabaseModelOverRTSL` is a wrapper for [RTSL](https://rteditor.battlehub.net/v350/manual/save-load.html). 2963 | 2964 | ## Projects Root Folder Path 2965 | 2966 | You can set the projects root folder in two ways: 2967 | 2968 | 1. By updating the `Project Root Folder Path` field of the Runtime Editor (script) (see RuntimeEditor.prefab/Runtime Editor (Script)/Extra Settings/Projects Root Folder Path). 2969 | 2970 | 2. Programmatically: 2971 | 2972 | ```csharp 2973 | using Battlehub.RTEditor; 2974 | using Battlehub.RTCommon; 2975 | 2976 | public class SetRootFolderExt : EditorExtension 2977 | { 2978 | protected override void OnInit() 2979 | { 2980 | var editor = IOC.Resolve<IRuntimeEditor>(); 2981 | editor.ProjectsRootFolderPath = @"C:\Runtime Editor Projects"; 2982 | } 2983 | } 2984 | ``` 2985 | 2986 | ## Manage Projects 2987 | 2988 | The following example demonstrates how to create, open, close, and delete projects using the Runtime Editor. 2989 | 2990 | The projects are stored in [Application.persistentDataPath](https://docs.unity3d.com/ScriptReference/Application-persistentDataPath.html) folder 2991 | ![Persistent Data Path][persistent_data_path] 2992 | 2993 | A typical project folder looks like this: 2994 | ![Asset Database Project Folder][asset_database_project_folder] 2995 | 2996 | ```csharp 2997 | using Battlehub.RTCommon; 2998 | using Battlehub.UIControls.MenuControl; 2999 | using System; 3000 | using UnityEngine; 3001 | 3002 | namespace Battlehub.RTEditor.Examples.Scene31 3003 | { 3004 | /// <summary> 3005 | /// Example on how to create, open, close, and delete projects. 3006 | /// </summary> 3007 | [MenuDefinition] 3008 | public class ProjectManagementExampleMenu : EditorExtension 3009 | { 3010 | private IWindowManager m_wm; 3011 | private IRuntimeEditor m_editor; 3012 | 3013 | protected override void OnInit() 3014 | { 3015 | base.OnInit(); 3016 | 3017 | m_wm = IOC.Resolve<IWindowManager>(); 3018 | m_editor = IOC.Resolve<IRuntimeEditor>(); 3019 | m_editor.BeforeLoadProject += OnBeforeLoadProject; 3020 | m_editor.LoadProject += OnLoadProject; 3021 | m_editor.BeforeUnloadProject += OnBeforeUnloadProject; 3022 | m_editor.UnloadProject += OnUnloadProject; 3023 | } 3024 | 3025 | protected override void OnCleanup() 3026 | { 3027 | base.OnCleanup(); 3028 | m_editor.BeforeLoadProject -= OnBeforeLoadProject; 3029 | m_editor.LoadProject -= OnLoadProject; 3030 | m_editor.BeforeUnloadProject -= OnBeforeUnloadProject; 3031 | m_editor.UnloadProject -= OnUnloadProject; 3032 | m_editor = null; 3033 | m_wm = null; 3034 | } 3035 | 3036 | [MenuCommand("Example/Manage Projects")] 3037 | public void ManageProjects() 3038 | { 3039 | m_wm.CreateWindow(BuiltInWindowNames.OpenProject); 3040 | } 3041 | 3042 | [MenuCommand("Example/Create Project")] 3043 | public void CreateProject() 3044 | { 3045 | m_wm.Prompt("Enter Project Name", "My Project", async (sender, args) => 3046 | { 3047 | using var b = m_editor.SetBusy(); 3048 | await m_editor.CreateProjectAsync(args.Text); 3049 | }); 3050 | } 3051 | 3052 | [MenuCommand("Example/Load Project")] 3053 | public void OpenProject() 3054 | { 3055 | m_wm.Prompt("Enter Project Name", "My Project", async (sender, args) => 3056 | { 3057 | using var b = m_editor.SetBusy(); 3058 | if (m_editor.IsProjectLoaded) 3059 | { 3060 | await m_editor.UnloadProjectAsync(); 3061 | } 3062 | await m_editor.LoadProjectAsync(args.Text); 3063 | }); 3064 | } 3065 | 3066 | [MenuCommand("Example/Delete Project")] 3067 | public void DeleteProject() 3068 | { 3069 | m_wm.Prompt("Enter Project Name", "My Project", async (sender, args) => 3070 | { 3071 | using var b = m_editor.SetBusy(); 3072 | await m_editor.DeleteProjectAsync(args.Text); 3073 | }); 3074 | } 3075 | 3076 | [MenuCommand("Example/Close Project")] 3077 | public async void CloseProject() 3078 | { 3079 | using var b = m_editor.SetBusy(); 3080 | await m_editor.UnloadProjectAsync(); 3081 | } 3082 | 3083 | private void OnBeforeLoadProject(object sender, EventArgs e) 3084 | { 3085 | Debug.Log($"On Before Load {m_editor.ProjectID}"); 3086 | } 3087 | 3088 | private void OnLoadProject(object sender, EventArgs e) 3089 | { 3090 | Debug.Log($"On Load {m_editor.ProjectID}"); 3091 | } 3092 | 3093 | private void OnBeforeUnloadProject(object sender, EventArgs e) 3094 | { 3095 | Debug.Log($"On Before Unload {m_editor.ProjectID}"); 3096 | } 3097 | 3098 | private void OnUnloadProject(object sender, EventArgs e) 3099 | { 3100 | Debug.Log($"On Unload {m_editor.ProjectID}"); 3101 | } 3102 | } 3103 | } 3104 | ``` 3105 | 3106 | ![Manage Projects][manage_projects_example] 3107 | ![Manage Projects][manage_projects_example_2] 3108 | 3109 | ## Import Export Project 3110 | 3111 | Install the [SharpZipLib package](https://docs.unity3d.com/Packages/com.unity.sharp-zip-lib@1.3/manual/index.html). In Package Manager, click on **"+ Add package by name"** and enter **"com.unity.sharp-zip-lib"** 3112 | 3113 | To export or import a project you can use following methods: 3114 | 3115 | ```csharp 3116 | private async Task ExportProjectAsync(string sourceProjectName, string archivePath) 3117 | { 3118 | if (File.Exists(archivePath)) 3119 | { 3120 | File.Delete(archivePath); 3121 | } 3122 | 3123 | using (var fs = File.OpenWrite(archivePath)) 3124 | { 3125 | var editor = IOC.Resolve<IRuntimeEditor>(); 3126 | using var b = editor.SetBusy(); 3127 | await editor.ExportProjectAsync(fs, sourceProjectName); 3128 | } 3129 | } 3130 | ``` 3131 | 3132 | ```csharp 3133 | private async Task ImportProjectAsync(string archivePath, string targetProjectName) 3134 | { 3135 | using (var fs = File.OpenRead(archivePath)) 3136 | { 3137 | var editor = IOC.Resolve<IRuntimeEditor>(); 3138 | using var b = editor.SetBusy(); 3139 | await editor.ImportProjectAsync(fs, targetProjectName); 3140 | } 3141 | } 3142 | ``` 3143 | 3144 | ## Create Folder 3145 | 3146 | The following example demonstrates how to create folders using the `IRuntimeEditor` interface in the Runtime Editor. 3147 | 3148 | ```csharp 3149 | using Battlehub.RTCommon; 3150 | using Battlehub.RTEditor; 3151 | using Battlehub.RTEditor.Models; 3152 | using Battlehub.UIControls.MenuControl; 3153 | using UnityEngine; 3154 | 3155 | [MenuDefinition] 3156 | public class CreateFolderExample : EditorExtension 3157 | { 3158 | private IRuntimeEditor m_editor; 3159 | 3160 | protected override void OnInit() 3161 | { 3162 | base.OnInit(); 3163 | 3164 | m_editor = IOC.Resolve<IRuntimeEditor>(); 3165 | m_editor.CreateFolder += OnCreateFolder; 3166 | } 3167 | 3168 | protected override void OnCleanup() 3169 | { 3170 | base.OnCleanup(); 3171 | m_editor.CreateFolder -= OnCreateFolder; 3172 | } 3173 | 3174 | [MenuCommand("Example/Create Folder")] 3175 | public void CreateFolder() 3176 | { 3177 | var wm = IOC.Resolve<IWindowManager>(); 3178 | wm.Prompt("Enter Folder Name", "New Folder", async (sender, args) => 3179 | { 3180 | string newFolderName = args.Text; 3181 | if (!m_editor.IsValidName(newFolderName)) 3182 | { 3183 | wm.MessageBox("Error", "Folder Name is invalid"); 3184 | return; 3185 | } 3186 | 3187 | // Use the current folder as a parent 3188 | var parentFolderID = m_editor.CurrentFolderID; 3189 | 3190 | // Make sure that the folder path is unique 3191 | string path = m_editor.GetUniquePath(parentFolderID, newFolderName); 3192 | 3193 | // Show busy indicator 3194 | using var b = m_editor.SetBusy(); 3195 | 3196 | await m_editor.CreateFolderAsync(path); 3197 | }); 3198 | } 3199 | 3200 | private void OnCreateFolder(object sender, CreateFolderEventArgs e) 3201 | { 3202 | // A folder is a special kind of asset and has an AssetID just like real assets 3203 | // The folder asset ID is temporary and it is different between sessions 3204 | ID assetID = e.AssetID; 3205 | 3206 | // You can get the folder path by its ID 3207 | var path = m_editor.GetPath(assetID); 3208 | 3209 | Debug.Log($"On Create Folder: {path}"); 3210 | } 3211 | } 3212 | 3213 | ``` 3214 | ## Set Current Folder 3215 | 3216 | The current folder is the folder selected in the project tree, whose contents are displayed in the right section of the asset database window. 3217 | 3218 | ```csharp 3219 | using Battlehub.RTCommon; 3220 | using Battlehub.RTEditor; 3221 | using Battlehub.RTEditor.Models; 3222 | using Battlehub.UIControls.MenuControl; 3223 | 3224 | [MenuDefinition] 3225 | public class SetCurrentFolderExample : EditorExtension 3226 | { 3227 | private IRuntimeEditor m_editor; 3228 | 3229 | protected override void OnInit() 3230 | { 3231 | base.OnInit(); 3232 | 3233 | m_editor = IOC.Resolve<IRuntimeEditor>(); 3234 | m_editor.LoadProject += OnLoadProject; 3235 | } 3236 | 3237 | protected override void OnCleanup() 3238 | { 3239 | base.OnCleanup(); 3240 | m_editor.LoadProject -= OnLoadProject; 3241 | } 3242 | 3243 | private async void OnLoadProject(object sender, System.EventArgs e) 3244 | { 3245 | using var b = m_editor.SetBusy(); 3246 | 3247 | var path = $"{m_editor.GetRootFolderPath()}/New Folder"; 3248 | 3249 | if (!m_editor.Exists(path)) 3250 | { 3251 | await m_editor.CreateFolderAsync(path); 3252 | } 3253 | 3254 | m_editor.CurrentFolderID = m_editor.GetAssetID(path); 3255 | } 3256 | } 3257 | ``` 3258 | 3259 | ## Runtime Scene 3260 | 3261 | ![HierarchyRoot][hierarchy_root] 3262 | 3263 | In the current version, the runtime editor requires that all runtime scene objects must be added to the `Scene` game object (the hierarchy root), which has the "Respawn" tag. 3264 | This ensures that the objects are visible in the hierarchy and can be saved as part of the scene. 3265 | 3266 | The exact value of the tag is specified by the `ExposeToEditor.HierarchyRootTag` field: 3267 | ```csharp 3268 | public class ExposeToEditor : MonoBehaviour 3269 | { 3270 | public static string HierarchyRootTag = "Respawn"; 3271 | } 3272 | ``` 3273 | To add a game object to the runtime editor scene programmatically, use the `AddGameObjectToScene` method: 3274 | ```csharp 3275 | var editor = IOC.Resolve<IRuntimeEditor>(); 3276 | 3277 | // Create a game object 3278 | var capsule = GameObject.CreatePrimitive(PrimitiveType.Capsule); 3279 | 3280 | // Add the game object to the scene 3281 | editor.AddGameObjectToScene(capsule); 3282 | 3283 | ``` 3284 | 3285 | ## Create New, Save, Load scene 3286 | 3287 | Here is an example of how to create, save, and open a scene in the Runtime Editor 3288 | 3289 | ```csharp 3290 | using Battlehub.RTCommon; 3291 | using Battlehub.RTEditor.Models; 3292 | using Battlehub.UIControls.MenuControl; 3293 | using UnityEngine; 3294 | 3295 | namespace Battlehub.RTEditor.Examples.Scene31 3296 | { 3297 | [MenuDefinition] 3298 | public class SceneExampleMenu : EditorExtension 3299 | { 3300 | private IRuntimeEditor m_editor; 3301 | private IWindowManager m_wm; 3302 | 3303 | protected override void OnInit() 3304 | { 3305 | base.OnInit(); 3306 | 3307 | m_editor = IOC.Resolve<IRuntimeEditor>(); 3308 | m_editor.InitializeNewScene += OnInitializeNewScene; 3309 | m_editor.BeforeSaveCurrentScene += OnBeforeSaveCurrentScene; 3310 | m_editor.SaveCurrentScene += OnSaveCurrentScene; 3311 | m_editor.BeforeOpenScene += OnBeforeOpenScene; 3312 | m_editor.OpenScene += OnOpenScene; 3313 | m_wm = IOC.Resolve<IWindowManager>(); 3314 | } 3315 | 3316 | protected override void OnCleanup() 3317 | { 3318 | base.OnCleanup(); 3319 | 3320 | m_editor.InitializeNewScene -= OnInitializeNewScene; 3321 | m_editor.BeforeSaveCurrentScene -= OnBeforeSaveCurrentScene; 3322 | m_editor.SaveCurrentScene -= OnSaveCurrentScene; 3323 | m_editor.BeforeOpenScene -= OnBeforeOpenScene; 3324 | m_editor.OpenScene -= OnOpenScene; 3325 | m_editor = null; 3326 | m_wm = null; 3327 | } 3328 | 3329 | 3330 | [MenuCommand("Example/New Scene")] 3331 | public async void InitializeNewScene() 3332 | { 3333 | using var b = m_editor.SetBusy(); 3334 | 3335 | if (m_editor.CanInitializeNewScene) 3336 | { 3337 | await m_editor.InitializeNewSceneAsync(); 3338 | } 3339 | else 3340 | { 3341 | m_wm.MessageBox("Error", "Can't initialize new scene"); 3342 | } 3343 | } 3344 | 3345 | private void OnInitializeNewScene(object sender, System.EventArgs e) 3346 | { 3347 | Debug.Log($"On Create New Scene {m_editor.CurrentScene.name} (ID: {m_editor.CurrentSceneID})"); 3348 | } 3349 | 3350 | private string GetScenePath(PromptDialogArgs args) 3351 | { 3352 | string currentFolderPath = m_editor.GetCurrentFolderPath(); 3353 | string sceneExt = m_editor.GetSceneExt(); 3354 | string scenePath = $"{currentFolderPath}/{args.Text}{sceneExt}"; 3355 | return scenePath; 3356 | } 3357 | 3358 | [MenuCommand("Example/Save Scene")] 3359 | public void SaveCurrentScene() 3360 | { 3361 | m_wm.Prompt("Enter Scene Name", "My Scene", async (sender, args) => 3362 | { 3363 | using var b = m_editor.SetBusy(); 3364 | 3365 | if (m_editor.CanSaveScene) 3366 | { 3367 | string scenePath = GetScenePath(args); 3368 | 3369 | await m_editor.SaveCurrentSceneAsync(scenePath); 3370 | } 3371 | else 3372 | { 3373 | m_wm.MessageBox("Error", "Can't save current scene"); 3374 | } 3375 | }); 3376 | } 3377 | 3378 | private void OnBeforeSaveCurrentScene(object sender, BeforeSaveSceneEventArgs e) 3379 | { 3380 | Debug.Log($"On Before Save Current Scene"); 3381 | } 3382 | 3383 | private void OnSaveCurrentScene(object sender, SaveSceneEventArgs e) 3384 | { 3385 | Debug.Log($"On Save Current Scene: {e.Scene.name} (ID: {e.SceneID} Path: {e.ScenePath})"); 3386 | } 3387 | 3388 | [MenuCommand("Example/Open Scene")] 3389 | public void OpenScene() 3390 | { 3391 | m_wm.Prompt("Enter Scene Name", "My Scene", async (sender, args) => 3392 | { 3393 | using var b = m_editor.SetBusy(); 3394 | 3395 | string scenePath = GetScenePath(args); 3396 | 3397 | if (m_editor.Exists(scenePath)) 3398 | { 3399 | await m_editor.OpenSceneAsync(scenePath); 3400 | } 3401 | else 3402 | { 3403 | m_wm.MessageBox("Error", $"Scene {scenePath} does not exist"); 3404 | } 3405 | }); 3406 | } 3407 | 3408 | private void OnBeforeOpenScene(object sender, AssetEventArgs e) 3409 | { 3410 | Debug.Log($"On Before Open Scene (ID: {e.AssetID})"); 3411 | } 3412 | 3413 | private void OnOpenScene(object sender, AssetEventArgs e) 3414 | { 3415 | Debug.Log($"On Open Scene (ID: {e.AssetID})"); 3416 | } 3417 | } 3418 | } 3419 | ``` 3420 | 3421 | ## Create, Save, Load, Delete Assets and Folders 3422 | 3423 | This is example on how to create, save, load, delete assets and folders 3424 | 3425 | ```csharp 3426 | 3427 | using Battlehub.RTCommon; 3428 | using Battlehub.RTEditor.Models; 3429 | using Battlehub.UIControls.MenuControl; 3430 | using System.Linq; 3431 | using UnityEngine; 3432 | 3433 | namespace Battlehub.RTEditor.Examples.Scene32 3434 | { 3435 | 3436 | [MenuDefinition] 3437 | public class FolderAndAssetExampleMenu : EditorExtension 3438 | { 3439 | private IRuntimeEditor m_editor; 3440 | private IWindowManager m_wm; 3441 | 3442 | protected override void OnInit() 3443 | { 3444 | m_editor = IOC.Resolve<IRuntimeEditor>(); 3445 | m_editor.CreateFolder += OnCreateFolder; 3446 | m_editor.CreateAsset += OnCreateAsset; 3447 | m_editor.SaveAsset += OnSaveAsset; 3448 | m_editor.DeleteAssets += OnDeleteAssets; 3449 | m_wm = IOC.Resolve<IWindowManager>(); 3450 | } 3451 | 3452 | protected override void OnCleanup() 3453 | { 3454 | m_editor.CreateFolder -= OnCreateFolder; 3455 | m_editor.CreateAsset -= OnCreateAsset; 3456 | m_editor.SaveAsset -= OnSaveAsset; 3457 | m_editor.DeleteAssets -= OnDeleteAssets; 3458 | m_editor = null; 3459 | m_wm = null; 3460 | } 3461 | 3462 | private string GetPath(string name, string ext = null) 3463 | { 3464 | string currentFolderPath = m_editor.GetCurrentFolderPath(); 3465 | return $"{currentFolderPath}/{name}{ext}"; 3466 | } 3467 | 3468 | [MenuCommand("Example/Create Folder")] 3469 | public void CreateFolder() 3470 | { 3471 | m_wm.Prompt("Enter Folder Name", "My Folder", async (sender, args) => 3472 | { 3473 | string path = GetPath(args.Text); 3474 | 3475 | if (!m_editor.Exists(path)) 3476 | { 3477 | using var b = m_editor.SetBusy(); 3478 | 3479 | await m_editor.CreateFolderAsync(path); 3480 | } 3481 | }); 3482 | } 3483 | 3484 | [MenuCommand("Example/Delete Folder")] 3485 | public void DeleteFolder() 3486 | { 3487 | m_wm.Prompt("Enter Folder Name", "My Folder", async (sender, args) => 3488 | { 3489 | string path = GetPath(args.Text); 3490 | 3491 | if (m_editor.Exists(path)) 3492 | { 3493 | using var b = m_editor.SetBusy(); 3494 | 3495 | await m_editor.DeleteAssetAsync(path); 3496 | } 3497 | else 3498 | { 3499 | m_wm.MessageBox("Error", 3500 | $"A folder named '{args.Text}' is not in the current folder"); 3501 | } 3502 | }); 3503 | } 3504 | 3505 | [MenuCommand("Example/Delete Current Folder", validate: true)] 3506 | public bool CanDeleteCurrentFolder() 3507 | { 3508 | return m_editor.CurrentFolderID != m_editor.RootFolderID; 3509 | } 3510 | 3511 | [MenuCommand("Example/Delete Current Folder")] 3512 | public async void DeleteCurrentFolder() 3513 | { 3514 | using var b = m_editor.SetBusy(); 3515 | 3516 | var currentFolderID = m_editor.CurrentFolderID; 3517 | await m_editor.DeleteAssetAsync(currentFolderID); 3518 | } 3519 | 3520 | [MenuCommand("Example/Create Prefab", validate: true)] 3521 | public bool CanCreatePrefab() 3522 | { 3523 | var go = m_editor.Selection.activeGameObject; 3524 | return m_editor.CanCreatePrefab(go); 3525 | } 3526 | 3527 | [MenuCommand("Example/Create Prefab")] 3528 | public async void CreatePrefab() 3529 | { 3530 | using var b = m_editor.SetBusy(); 3531 | 3532 | var go = m_editor.Selection.activeGameObject; 3533 | await m_editor.CreateAssetAsync(go); 3534 | } 3535 | 3536 | [MenuCommand("Example/Create Material")] 3537 | public async void CreateMaterial() 3538 | { 3539 | var material = Instantiate(RenderPipelineInfo.DefaultMaterial); 3540 | material.name = "Material"; 3541 | material.Color(Color.green); 3542 | material.MainTexture(Resources.Load<Texture2D>("Duck")); 3543 | 3544 | using var b = m_editor.SetBusy(); 3545 | await m_editor.CreateAssetAsync(material); 3546 | } 3547 | 3548 | 3549 | [MenuCommand("Example/Update Material", validate: true)] 3550 | public bool CanUpdateMaterial() 3551 | { 3552 | return m_editor.Selection.activeObject is Material; 3553 | } 3554 | 3555 | [MenuCommand("Example/Update Material")] 3556 | public async void UpdateMaterial() 3557 | { 3558 | Material material = (Material)m_editor.Selection.activeObject; 3559 | material.Color(Random.ColorHSV()); 3560 | 3561 | ID assetID = m_editor.GetAssetID(material); 3562 | if (assetID != ID.Empty) 3563 | { 3564 | using var b = m_editor.SetBusy(); 3565 | await m_editor.SaveAssetAsync(assetID); 3566 | } 3567 | } 3568 | 3569 | [MenuCommand("Example/Delete Selected", validate: true)] 3570 | public bool CanDeleteSelected() 3571 | { 3572 | return m_editor.SelectedAssets.Length > 0 && 3573 | !m_editor.SelectedAssets.Contains(m_editor.RootFolderID); 3574 | } 3575 | 3576 | [MenuCommand("Example/Delete Selected")] 3577 | public async void DeleteSelected() 3578 | { 3579 | using var b = m_editor.SetBusy(); 3580 | var selection = m_editor.SelectedAssets; 3581 | await m_editor.DeleteAssetsAsync(selection); 3582 | } 3583 | 3584 | private void OnCreateFolder(object sender, CreateFolderEventArgs e) 3585 | { 3586 | Debug.Log( 3587 | $"On Create Folder (ID: {e.AssetID} Path: {m_editor.GetPath(e.AssetID)})"); 3588 | } 3589 | 3590 | private void OnCreateAsset(object sender, CreateAssetEventArgs e) 3591 | { 3592 | Debug.Log( 3593 | $"On Create Asset (ID: {e.AssetID} Path: {m_editor.GetPath(e.AssetID)})"); 3594 | } 3595 | 3596 | private void OnSaveAsset(object sender, SaveAssetEventArgs e) 3597 | { 3598 | Debug.Log( 3599 | $"On Save Asset (ID: {e.AssetID} Path: {m_editor.GetPath(e.AssetID)})"); 3600 | } 3601 | 3602 | private void OnDeleteAssets(object sender, DeleteAssetsEventArgs e) 3603 | { 3604 | for (int i = 0; i < e.AssetID.Count; ++i) 3605 | { 3606 | ID assetID = e.AssetID[i]; 3607 | Debug.Log( 3608 | $"On Delete Asset (ID: {assetID}, Children: {e.ChildrenID[i].Count})"); 3609 | } 3610 | } 3611 | } 3612 | } 3613 | 3614 | ``` 3615 | 3616 | ## Instantiate asset 3617 | 3618 | This is an example of creating and instantiating an asset. 3619 | 3620 | ```csharp 3621 | using Battlehub.RTCommon; 3622 | using Battlehub.RTEditor; 3623 | using Battlehub.UIControls.MenuControl; 3624 | using System.Linq; 3625 | using UnityEngine; 3626 | 3627 | [MenuDefinition] 3628 | public class InstantiateAssetExample : MonoBehaviour 3629 | { 3630 | [MenuCommand("Example/Create Asset")] 3631 | public async void Create() 3632 | { 3633 | var editor = IOC.Resolve<IRuntimeEditor>(); 3634 | 3635 | // Create game object 3636 | var capsule = GameObject.CreatePrimitive(PrimitiveType.Capsule); 3637 | editor.AddGameObjectToScene(capsule, select:false); 3638 | 3639 | // Expose to editor 3640 | capsule.AddComponent<ExposeToEditor>(); 3641 | 3642 | // Get .prefab extension 3643 | var ext = editor.GetExt(capsule); 3644 | 3645 | // Get unique asset path 3646 | var path = editor.GetUniquePath($"Capsule{ext}"); 3647 | 3648 | // Create asset 3649 | await editor.CreateAssetAsync(capsule, path); 3650 | 3651 | // Select create asset 3652 | editor.SelectedAssets = new[] { editor.GetAssetID(path) }; 3653 | } 3654 | 3655 | [MenuCommand("Example/Instantiate Asset", validate:true)] 3656 | public bool CanInstanate() 3657 | { 3658 | var editor = IOC.Resolve<IRuntimeEditor>(); 3659 | return editor.SelectedAssets.Any(asset => editor.CanInstantiateAsset(asset)); 3660 | } 3661 | 3662 | 3663 | [MenuCommand("Example/Instantiate Asset")] 3664 | public async void Instanate() 3665 | { 3666 | var editor = IOC.Resolve<IRuntimeEditor>(); 3667 | using var b = editor.SetBusy(); 3668 | 3669 | // Instantiate selected assets 3670 | var result = await editor.InstantiateAssetsAsync(editor.SelectedAssets); 3671 | 3672 | // Select instances 3673 | editor.Selection.objects = result.Instances; 3674 | } 3675 | } 3676 | ``` 3677 | 3678 | ![Instanate Asset][instantiate_asset_example] 3679 | 3680 | ## Duplicate Asset 3681 | 3682 | You can duplicate any asset or folder you select in the project view. 3683 | 3684 | ```csharp 3685 | using Battlehub.RTCommon; 3686 | using Battlehub.RTEditor; 3687 | using Battlehub.RTEditor.Models; 3688 | using Battlehub.UIControls.MenuControl; 3689 | using UnityEngine; 3690 | 3691 | [MenuDefinition] 3692 | public class DuplicateAssetExample : MonoBehaviour 3693 | { 3694 | [MenuCommand("Example/Duplicate Asset", validate:true)] 3695 | public bool CanDuplicate() 3696 | { 3697 | var editor = IOC.Resolve<IRuntimeEditor>(); 3698 | return editor.SelectedAssets.Length > 0 && 3699 | editor.CanDuplicateAsset(editor.SelectedAssets[0]); 3700 | } 3701 | 3702 | 3703 | [MenuCommand("Example/Duplicate Asset")] 3704 | public async void Dupicate() 3705 | { 3706 | var editor = IOC.Resolve<IRuntimeEditor>(); 3707 | 3708 | var assetID = editor.SelectedAssets[0]; 3709 | string path = editor.GetPath(assetID); 3710 | string targetPath = editor.GetUniquePath(path); 3711 | 3712 | using var b = editor.SetBusy(); 3713 | await editor.DuplicateAssetAsync(assetID, targetPath); 3714 | } 3715 | } 3716 | ``` 3717 | 3718 | ## Move Asset 3719 | 3720 | This example shows how to move an asset. You can rename assets using the same approach. 3721 | 3722 | ```csharp 3723 | using Battlehub.RTCommon; 3724 | using Battlehub.RTEditor; 3725 | using Battlehub.RTEditor.Models; 3726 | using Battlehub.UIControls.MenuControl; 3727 | using UnityEngine; 3728 | 3729 | [MenuDefinition] 3730 | public class MoveAssetExample : MonoBehaviour 3731 | { 3732 | [MenuCommand("Example/Move Asset", validate: true)] 3733 | public bool CanMoveAsset() 3734 | { 3735 | var editor = IOC.Resolve<IRuntimeEditor>(); 3736 | return editor.SelectedAssets.Length > 0; 3737 | } 3738 | 3739 | 3740 | [MenuCommand("Example/Move Asset")] 3741 | public async void MoveAsset() 3742 | { 3743 | var editor = IOC.Resolve<IRuntimeEditor>(); 3744 | 3745 | var assetID = editor.SelectedAssets[0]; 3746 | string path = editor.GetPath(assetID); 3747 | string targetPath = editor.GetUniquePath(path); 3748 | 3749 | using var b = editor.SetBusy(); 3750 | await editor.MoveAssetAsync(editor.SelectedAssets[0], targetPath); 3751 | } 3752 | } 3753 | ``` 3754 | 3755 | ## Import Export Assets 3756 | 3757 | Install the [SharpZipLib package](https://docs.unity3d.com/Packages/com.unity.sharp-zip-lib@1.3/manual/index.html). In Package Manager, click on **"+ Add package by name"** and enter **"com.unity.sharp-zip-lib"** 3758 | 3759 | ```csharp 3760 | private async Task ExportAssetsAsync(string path) 3761 | { 3762 | if (File.Exists(path)) 3763 | { 3764 | File.Delete(path); 3765 | } 3766 | 3767 | using (var fs = File.OpenWrite(path)) 3768 | { 3769 | var editor = IOC.Resolve<IRuntimeEditor>(); 3770 | 3771 | using var b = editor.SetBusy(); 3772 | await editor.ExportAssetsAsync(editor.SelectedAssets, fs, includeDependencies:true); 3773 | } 3774 | } 3775 | 3776 | 3777 | private async Task ImportAssetsAsync(string path) 3778 | { 3779 | using (var fs = File.OpenRead(path)) 3780 | { 3781 | var editor = IOC.Resolve<IRuntimeEditor>(); 3782 | 3783 | using var b = editor.SetBusy(); 3784 | await editor.ImportAssetsAsync(fs); 3785 | } 3786 | } 3787 | ``` 3788 | 3789 | > **Note** 3790 | > For complete example, see **Scene33 - Export And Import Asset** in the [Example Scenes section](#example-scenes). 3791 | 3792 | 3793 | 3794 | ## External Asset Loaders 3795 | 3796 | External asset loaders are used to load external assets imported into a project. 3797 | An external asset is an asset loaded into the project using a specific loader, such as the Addressable Loader, a loader that loads assets from the Resources folder, a GLB loader, or any other third-party loader. 3798 | External assets are read-only and contain only identifiers for parts within the data file. If you need to make edits to an external asset, you can create an asset variant of it. 3799 | 3800 | There are four built-in external asset loaders: 3801 | 3802 | - **Resources Loader** 3803 | Located at `Assets\Battlehub\RTEditor\Runtime\RTEditor\Models\AssetDatabaseImportSources\ResourcesLoaderModel.cs` 3804 | 3805 | - **Addressables Loader** 3806 | Located at `Assets\Battlehub\RTEditor\Runtime\RTEditor\Models\AssetDatabaseImportSources\AddressablesLoaderModel.cs` 3807 | > **Important:** You must install **com.unity.addressables** to use this loader. For more information, refer to the [Unity Addressables documentation](https://docs.unity3d.com/Packages/com.unity.addressables@1.21/manual/LoadingAddressableAssets.html). 3808 | 3809 | - **GLTFast Loader** 3810 | Located at `Assets\Battlehub\RTImporter\Runtime\Importers\GltfImporter.cs` 3811 | > **Important:** You must install **com.unity.cloud.gltfast** to use this loader. For more information, refer to the [GLTFast documentation](https://docs.unity3d.com/Packages/com.unity.cloud.gltfast@6.6/manual/index.html). 3812 | 3813 | - **DemoGameAssets Loader** 3814 | Located at `Assets\Battlehub\RTEditorDemo\Examples\RTEditor DemoGame\DemoGameAssetsLoader.cs` 3815 | 3816 | To create your own loader, implement the **IExternalAssetLoaderModel** interface: 3817 | 3818 | ```csharp 3819 | using Battlehub.RTEditor.Models; 3820 | using System; 3821 | using System.Collections.Generic; 3822 | using System.Threading.Tasks; 3823 | using UnityEngine; 3824 | 3825 | using UnityObject = UnityEngine.Object; 3826 | public class MyLoader : IExternalAssetLoaderModel 3827 | { 3828 | public string LoaderID => nameof(MyLoader); 3829 | 3830 | public Task<object> LoadAsync(string key, object root, IProgress<float> progress = null) 3831 | { 3832 | var parent = root as Transform; 3833 | 3834 | GameObject asset; 3835 | switch(key) 3836 | { 3837 | case "Cube": 3838 | asset = GameObject.CreatePrimitive(PrimitiveType.Cube); 3839 | break; 3840 | case "Capsule": 3841 | asset = GameObject.CreatePrimitive(PrimitiveType.Capsule); 3842 | break; 3843 | default: 3844 | throw new KeyNotFoundException(key); 3845 | } 3846 | 3847 | asset.transform.SetParent(parent, false); 3848 | 3849 | return Task.FromResult<object>(asset); 3850 | } 3851 | 3852 | public void Release(object obj) 3853 | { 3854 | var asset = obj as UnityObject; 3855 | if (asset != null) 3856 | { 3857 | UnityObject.Destroy(asset); 3858 | } 3859 | } 3860 | } 3861 | 3862 | ``` 3863 | 3864 | To register your custom loader, use the AddExternalAssetLoader method: 3865 | 3866 | ```csharp 3867 | using Battlehub.RTCommon; 3868 | using Battlehub.RTEditor; 3869 | using Battlehub.RTEditor.Models; 3870 | 3871 | public class RegisterMyLoader : EditorExtension 3872 | { 3873 | private IRuntimeEditor m_editor; 3874 | private IExternalAssetLoaderModel m_loader; 3875 | 3876 | protected override void OnInit() 3877 | { 3878 | m_editor = IOC.Resolve<IRuntimeEditor>(); 3879 | 3880 | m_loader = new MyLoader(); 3881 | 3882 | m_editor.AddExternalAssetLoader(m_loader); 3883 | } 3884 | 3885 | protected override void OnCleanup() 3886 | { 3887 | m_editor.RemoveExternalAssetLoader(m_loader); 3888 | m_editor = null; 3889 | } 3890 | } 3891 | ``` 3892 | 3893 | After registering the custom loader, you can use the ImportExternalAssetAsync method to import an external asset by providing the loader ID and key to the method: 3894 | 3895 | ```csharp 3896 | using var b = m_editor.SetBusy(); 3897 | await m_editor.ImportExternalAssetAsync(m_editor.RootFolderID, key: "Cube", loaderID: nameof(MyLoader), desiredName: "My External Asset"); 3898 | ``` 3899 | 3900 | ![Custom External Asset Loader][custom_external_asset_loader] 3901 | 3902 | > **Note** <br/> 3903 | For the **Resources Loader**, you should use the relative path in the Resources folder as the key (this is the same as the path parameter in the [Resources.Load method](https://docs.unity3d.com/ScriptReference/Resources.Load.html)).<br/> 3904 | For the **Addressables Loader**, the key will be used as a parameter for the [Addressables.LoadAsset method](https://docs.unity3d.com/Packages/com.unity.addressables@1.20/api/UnityEngine.AddressableAssets.Addressables.LoadAsset.html#UnityEngine_AddressableAssets_Addressables_LoadAsset__1_System_Object_).<br/> 3905 | For the **GLTFastLoader Loader**, the key can be an absolute URI, an absolute path, or a path relative to the **.Library** folder inside the runtime project folder.<br/> 3906 | 3907 | ### TriLibLoader Example 3908 | 3909 | The Runtime Editor is only able to load .glb and .gltf models. For .fbx and other formats, you need to use an asset such as [TriLib](https://assetstore.unity.com/packages/tools/modeling/trilib-2-model-loading-package-157548). Here is an example of how to create an external asset loader using the TriLib library and integrate it with the Runtime Editor. The procedure is almost the same as for any other loader. 3910 | 3911 | #### Implement IExternalAssetLoaderModel 3912 | 3913 | ```csharp 3914 | using Battlehub.RTCommon; 3915 | using Battlehub.RTEditor.Models; 3916 | using System; 3917 | using System.IO; 3918 | using System.Threading.Tasks; 3919 | using TriLibCore; 3920 | using UnityEngine; 3921 | 3922 | public class TriLibLoaderModel : IExternalAssetLoaderModel 3923 | { 3924 | public string LoaderID => nameof(TriLibLoaderModel); 3925 | 3926 | public string LibraryFolder { get; set; } 3927 | 3928 | private Task<GameObject> LoadUsingTriLibAsync(string filePath, Transform root) 3929 | { 3930 | TaskCompletionSource<GameObject> tcs = new TaskCompletionSource<GameObject>(); 3931 | GameObject go = null; 3932 | var options = AssetLoader.CreateDefaultLoaderOptions(); 3933 | options.AddAssetUnloader = false; 3934 | 3935 | AssetLoader.LoadModelFromFile(filePath, ctx => 3936 | { 3937 | go = ctx.RootGameObject; 3938 | go.transform.SetParent(root); 3939 | }, 3940 | ctx => 3941 | { 3942 | tcs.SetResult(go); 3943 | }, 3944 | (ctx, progress) => { }, 3945 | err => 3946 | { 3947 | tcs.SetException(err.GetInnerException()); 3948 | }, 3949 | null, options); 3950 | 3951 | return tcs.Task; 3952 | } 3953 | 3954 | public async Task<object> LoadAsync(string key, object root, IProgress<float> progress = null) 3955 | { 3956 | string path; 3957 | if (Uri.TryCreate(key, UriKind.Absolute, out _) || Path.IsPathRooted(key)) 3958 | { 3959 | path = key; 3960 | } 3961 | else 3962 | { 3963 | path = !string.IsNullOrEmpty(LibraryFolder) ? $"{LibraryFolder}/{key}" : key; 3964 | } 3965 | 3966 | var go = await LoadUsingTriLibAsync(path, root as Transform); 3967 | var parts = go.GetComponentsInChildren<Transform>(true); 3968 | foreach (var part in parts) 3969 | { 3970 | part.gameObject.AddComponent<ExposeToEditor>(); 3971 | } 3972 | 3973 | return go; 3974 | } 3975 | 3976 | public void Release(object obj) 3977 | { 3978 | GameObject go = obj as GameObject; 3979 | if (go != null) 3980 | { 3981 | UnityEngine.Object.Destroy(go); 3982 | } 3983 | } 3984 | } 3985 | ``` 3986 | 3987 | #### Register TriLibLoader 3988 | 3989 | ```csharp 3990 | using Battlehub.RTCommon; 3991 | using Battlehub.RTEditor; 3992 | using Battlehub.RTEditor.Models; 3993 | 3994 | public class RegisterTriLibLoader : EditorExtension 3995 | { 3996 | private IRuntimeEditor m_editor; 3997 | private IExternalAssetLoaderModel m_loader; 3998 | 3999 | protected override void OnInit() 4000 | { 4001 | m_editor = IOC.Resolve<IRuntimeEditor>(); 4002 | m_loader = new TriLibLoaderModel(); 4003 | m_editor.AddExternalAssetLoader(m_loader); 4004 | } 4005 | 4006 | protected override void OnCleanup() 4007 | { 4008 | m_editor.RemoveExternalAssetLoader(m_loader); 4009 | m_editor = null; 4010 | } 4011 | } 4012 | ``` 4013 | 4014 | #### Import FBX as External Asset 4015 | 4016 | ```csharp 4017 | using var b = m_editor.SetBusy(); 4018 | 4019 | string key = "F:/ModelsCollection/ikea desk.fbx"; 4020 | string loaderID = nameof(TriLibLoaderModel); 4021 | string name = "My Model"; 4022 | 4023 | await m_editor.ImportExternalAssetAsync(m_editor.RootFolderID, key, loaderID, name); 4024 | ``` 4025 | 4026 | ![TriLib Asset Loader][trilib_loader] 4027 | 4028 | 4029 | ## Import Sources 4030 | 4031 | If you want to allow the user to select which assets to import and import them themselves, you should create import sources. 4032 | 4033 | There are three built-in import sources: 4034 | - **Addressables Import Source:** To use Addressables Import Source, you should install the [**com.unity.addressables**](https://docs.unity3d.com/Packages/com.unity.addressables@1.20/manual/index.html) package. 4035 | - **Resources Import Source** 4036 | - **DemoGame Import Source** 4037 | 4038 | In the Runtime Editor, if you click `File` > `Import Assets`, the asset database import source window will open, and you will see a dropdown with all available import sources. 4039 | 4040 | 1. **Select an import source from the dropdown.** 4041 | 2. The list of asset groups will be loaded (by default, there is only one Default group in all built-in sources). 4042 | 3. Select a group and click OK. 4043 | 4. You will be navigated to the asset database import view. 4044 | 5. Select assets and click Import. 4045 | 4046 | The `IRuntimeEditor.ImportExternalAssetAsync` method will be called for each of the selected assets with the corresponding key and loader ID. 4047 | 4048 | ![Open Import Sources window][import_sources_1] 4049 | ![Select Import Source][import_sources_2] 4050 | ![Select Import Group][import_sources_3] 4051 | ![Select Import Assets][import_sources_4] 4052 | ![Import Assets][import_sources_5] 4053 | 4054 | ### Resources Import Source 4055 | 4056 | To access built-in import sources and modify groups and assets, follow these steps: 4057 | 4058 | 1. **Create Asset Database** 4059 | - Click `Tools` -> `Runtime Editor` -> `Create Asset Database` 4060 | ![Create Asset Database][modify_import_source_1] 4061 | 4062 | 2. **Collect Scene Assets (Optional)** 4063 | - If you want assets referenced from the scene to be collected into the SceneAssetsList and [registered as external assets](https://github.com/Battlehub0x/RuntimeAssetDatabase?tab=readme-ov-file#register-external-asset), click Save. Otherwise, click Cancel. 4064 | ![Scene Assets List][modify_import_source_2] 4065 | 4066 | 3. **Add Assets or Groups** 4067 | - Add more assets or groups. The Group key in the groups list must match the Group key in the assets list. The Asset Key must be equal to the path relative to the Resources folder. 4068 | ![Add Assets or Groups][modify_import_source_3] 4069 | 4070 | > **Note** 4071 | > 4072 | > It is possible to do the same programmatically. The `ResourcesImportSourceModel` has two public methods: 4073 | > ```csharp 4074 | > void ResourcesImportSourceModel.SetGroups(ImportGroup[] groups) 4075 | > void ResourcesImportSourceModel.SetAssets(ImportAsset[] assets) 4076 | > ``` 4077 | The `ImportGroup` and `ImportAsset` classes are used to define the groups and assets to be imported. 4078 | 4079 | ### Addressables Import Source 4080 | 4081 | 1. **Install the Addressables Package** 4082 | - Install the [**com.unity.addressables**](https://docs.unity3d.com/Packages/com.unity.addressables@1.20/manual/index.html) package. 4083 | 4084 | 2. **Create Addressables Settings** 4085 | - Navigate to `Window` > `Asset Management` > `Addressables` > `Groups` (click `Create Addressables Settings` if needed). 4086 | 4087 | 3. **Make Prefabs Addressable** 4088 | - Make some prefabs addressable by selecting Addressable checkbox. Assign the "default" label. 4089 | ![Make some prefab addressable][import_source_addressable_1] 4090 | 4091 | 4. **Create a New Build (Optional)** 4092 | - Optionally, switch the PlayMode script to "Use Existing Build" and create a new build. 4093 | 4094 | 5. **Configure AssetDatabaseModel** 4095 | - Select the `AssetDatabaseModel` game object (If you don't have it in the scene, add it as described [here](#resources-import-source)). 4096 | - Ensure the group key matches the label of the addressables you want to show in that group during import. 4097 | ![Configure AssetDatabaseModel][import_source_addressable_2] 4098 | ![Import Addressable][import_source_addressable_3] 4099 | 4100 | ### Custom Import Source 4101 | 4102 | To create your own import source, follow these steps: 4103 | 4104 | 1. **Implement the `IImportSourceModel` Interface** 4105 | 4106 | ```csharp 4107 | using Battlehub.RTEditor.Models; 4108 | using System.Threading.Tasks; 4109 | 4110 | public class MyImportSource : IImportSourceModel 4111 | { 4112 | public bool IsEnabled => true; 4113 | 4114 | public int SortIndex => 0; 4115 | 4116 | public string DisplayName => "My Import Source"; 4117 | 4118 | public string LoaderID => nameof(MyLoader); 4119 | 4120 | public Task<IImportGroup[]> GetGroupsAsync() 4121 | { 4122 | IImportGroup[] groups = new[] 4123 | { 4124 | new ImportGroup( 4125 | key: "DefaultGroupKey", 4126 | name: "My Group") 4127 | }; 4128 | return Task.FromResult(groups); 4129 | } 4130 | 4131 | public Task<IImportAsset[]> GetAssetsAsync(string groupKey) 4132 | { 4133 | var assets = new[] 4134 | { 4135 | new ImportAsset("DefaultGroupKey", "Cube", "My Cube"), 4136 | new ImportAsset("DefaultGroupKey", "Capsule", "My Capsule"), 4137 | }; 4138 | 4139 | return Task.FromResult<IImportAsset[]>(assets); 4140 | } 4141 | } 4142 | ``` 4143 | 4144 | 2. **Register the Custom Import Source** 4145 | 4146 | ```csharp 4147 | using Battlehub.RTCommon; 4148 | using Battlehub.RTEditor; 4149 | 4150 | public class RegisterMyImportSource : EditorExtension 4151 | { 4152 | private IRuntimeEditor m_editor; 4153 | private MyImportSource m_importSource; 4154 | 4155 | protected override void OnInit() 4156 | { 4157 | m_importSource = new MyImportSource(); 4158 | m_editor = IOC.Resolve<IRuntimeEditor>(); 4159 | m_editor.AddImportSource(m_importSource); 4160 | } 4161 | 4162 | protected override void OnCleanup() 4163 | { 4164 | m_editor.RemoveImportSource(m_importSource); 4165 | m_importSource = null; 4166 | m_editor = null; 4167 | } 4168 | } 4169 | ``` 4170 | 4171 | 3. **Use the Custom Import Source** 4172 | 4173 | Once registered, your custom import source will be available in the Runtime Editor's asset import dropdown. Users can select it and import assets as configured. 4174 | 4175 | ![Custom Import Source][my_import_source_1] 4176 | ![Custom Import Source][my_import_source_2] 4177 | 4178 | > **Note** 4179 | > 4180 | > In this example, `MyLoader` from [External Asset Loaders](#external-asset-loaders) is being used. Ensure you have implemented and registered `MyLoader` as described in the previous sections. 4181 | 4182 | ## File Importers 4183 | 4184 | File importers come into play when you open the file picker using `File` -> `Import From File` in the Runtime Editor menu. 4185 | 4186 | ![Open File Picker][file_importers_1] 4187 | ![File Picker][file_importers_2] 4188 | 4189 | The file picker determines which types of files to show by inspecting the supported extensions of available file importers. 4190 | 4191 | There are four built-in file importers: 4192 | 4193 | - **GlbImporter** 4194 | - **GltfImporter** 4195 | - **PngImporter** 4196 | - **JpgImporter** 4197 | 4198 | > **Note** 4199 | > To enable `GlbImporter` and `GltfImporter`, you should install **com.unity.cloud.gltfast**. For more information, refer to the [GLTFast documentation](https://docs.unity3d.com/Packages/com.unity.cloud.gltfast@6.6/manual/index.html). 4200 | 4201 | 4202 | ### Custom File Importer 4203 | 4204 | To create a custom file importer, you need to create a class derived from `AssetDatabaseFileImporter`. 4205 | Override the `FileExt` field to return the supported file extension and implement the `ImportAsync` method. 4206 | 4207 | In the example below, the `TriLibLoader` from the [TriLib Loader Example](#trilibloader-example) is used to load an external asset. 4208 | Imported models are cached in the `.Library` folder. 4209 | 4210 | > **Note** 4211 | > You don't have to use loaders in the import method; you can simply use the IRuntimeEditor.CreateAsset method to create a normal asset, not an external one. 4212 | 4213 | ```csharp 4214 | using Battlehub.RTEditor; 4215 | using Battlehub.RTEditor.Models; 4216 | using System; 4217 | using System.Threading; 4218 | using System.Threading.Tasks; 4219 | 4220 | public class FbxFileImporter : AssetDatabaseFileImporter 4221 | { 4222 | public override int Priority 4223 | { 4224 | get { return int.MinValue; } 4225 | } 4226 | 4227 | public override string FileExt 4228 | { 4229 | get { return ".fbx"; } 4230 | } 4231 | 4232 | public override string IconPath 4233 | { 4234 | get { return "Importers/Fbx"; } 4235 | } 4236 | 4237 | public override async Task ImportAsync(string filePath, string targetPath, CancellationToken cancelToken) 4238 | { 4239 | try 4240 | { 4241 | Guid assetID = Guid.NewGuid(); 4242 | string externalAssetKey = AddFileToLibrary(filePath, assetID); 4243 | await ImportExternalAsset(targetPath, assetID, externalAssetKey, m_assetLoader); 4244 | } 4245 | catch (Exception e) 4246 | { 4247 | throw new FileImporterException(e.Message, e); 4248 | } 4249 | } 4250 | 4251 | private IExternalAssetLoaderModel m_assetLoader; 4252 | public override void Load() 4253 | { 4254 | base.Load(); 4255 | 4256 | var triLibLoader = new TriLibLoaderModel 4257 | { 4258 | LibraryFolder = Editor.LibraryRootFolder 4259 | }; 4260 | m_assetLoader = triLibLoader; 4261 | 4262 | Editor.AddExternalAssetLoader(m_assetLoader.LoaderID, m_assetLoader); 4263 | } 4264 | 4265 | public override void Unload() 4266 | { 4267 | Editor.RemoveExternalAssetLoader(m_assetLoader.LoaderID); 4268 | m_assetLoader = null; 4269 | 4270 | base.Unload(); 4271 | } 4272 | } 4273 | ``` 4274 | 4275 | ## Web Storage Sample 4276 | 4277 | You can use the runtime asset database in conjunction with an HTTP web server, in WebGL and Standalone builds. 4278 | 4279 | To start the web sample, follow these steps: 4280 | 4281 | 1. Install the [Newtonsoft Json package]((https://docs.unity3d.com/Packages/com.unity.nuget.newtonsoft-json@3.2/manual/index.html)). In Package Manager, click on "+ Add package by name" and enter "com.unity.nuget.newtonsoft-json" 4282 | 2. Unpack the `Asset/Battlehub/Storage.Web` Unity package. 4283 | 3. Open the `Asset/Battlehub.Extensions/Storage.Web/SampleScene` Unity scene. 4284 | 4. Extract `Asset/Battlehub.Extensions/Storage.Web/SampleHttpServer.zip` to a folder (e.g., `C:\SampleHttpWebServer`). 4285 | 5. Install Node.js from [Node.js](https://nodejs.org/en/learn/getting-started/how-to-install-nodejs). 4286 | 6. Open a terminal and navigate to `C:\SampleHttpWebServer`. 4287 | 7. Run the command `npm install`. 4288 | 8. Run the command `node app.js`. 4289 | 9. Enter play mode in Unity. 4290 | 10. The projects will be created in the `Project Root Folder Path` (see SampleScene/RuntimeEditor/Runtime Editor (Script)/Extra Settings/Projects Root Folder Path). 4291 | 4292 | 4293 | ## Serializer Extensions 4294 | Surrogates are intermediary classes used by the Serializer to facilitate the reading and writing of data to Unity objects during serialization. **To enable the serialization of a specific class, you must create a surrogate for it**. These surrogates can be generated automatically or created from scratch. To generate a Surrogate class, you can use the "Create Surrogates" window. 4295 | 4296 | ![Generate Surrogate][generate_surrogate] 4297 | 4298 | Sample component: 4299 | 4300 | ```C# 4301 | using UnityEngine; 4302 | 4303 | public class MyComponent : MonoBehaviour 4304 | { 4305 | public Material Material; 4306 | 4307 | public GameObject Target; 4308 | 4309 | public int IntValue; 4310 | 4311 | public string StringValue; 4312 | } 4313 | ``` 4314 | 4315 | ![Surrogates Folder][surrogates_folder] 4316 | 4317 | ```C# 4318 | using ProtoBuf; 4319 | using System; 4320 | using System.Threading.Tasks; 4321 | 4322 | namespace Battlehub.Storage.Surrogates 4323 | { 4324 | [ProtoContract] 4325 | [Surrogate(typeof(global::MyComponent), _PROPERTY_INDEX, _TYPE_INDEX)] 4326 | public class MyComponentSurrogate<TID> : ISurrogate<TID> where TID : IEquatable<TID> 4327 | { 4328 | const int _PROPERTY_INDEX = 7; 4329 | const int _TYPE_INDEX = 153; 4330 | //_PLACEHOLDER_FOR_EXTENSIONS_DO_NOT_DELETE_OR_CHANGE_THIS_LINE_PLEASE 4331 | 4332 | [ProtoMember(2)] 4333 | public TID id { get; set; } 4334 | 4335 | [ProtoMember(3)] 4336 | public TID gameObjectId { get; set; } 4337 | 4338 | [ProtoMember(4))] 4339 | public global::System.Boolean enabled { get; set; } 4340 | 4341 | [ProtoMember(5)] 4342 | public TID Material { get; set; } 4343 | 4344 | [ProtoMember(6)] 4345 | public TID Target { get; set; } 4346 | 4347 | [ProtoMember(7))] 4348 | public global::System.Int32 IntValue { get; set; } 4349 | //_PLACEHOLDER_FOR_NEW_PROPERTIES_DO_NOT_DELETE_OR_CHANGE_THIS_LINE_PLEASE 4350 | 4351 | public ValueTask Serialize(object obj, ISerializationContext<TID> ctx) 4352 | { 4353 | var idmap = ctx.IDMap; 4354 | 4355 | var o = (global::MyComponent)obj; 4356 | id = idmap.GetOrCreateID(o); 4357 | gameObjectId = idmap.GetOrCreateID(o.gameObject); 4358 | enabled = o.enabled; 4359 | Material = idmap.GetOrCreateID(o.Material); 4360 | Target = idmap.GetOrCreateID(o.Target); 4361 | IntValue = o.IntValue; 4362 | //_PLACEHOLDER_FOR_SERIALIZE_METHOD_BODY_DO_NOT_DELETE_OR_CHANGE_THIS_LINE_PLEASE 4363 | 4364 | return default; 4365 | } 4366 | 4367 | public ValueTask<object> Deserialize(ISerializationContext<TID> ctx) 4368 | { 4369 | var idmap = ctx.IDMap; 4370 | 4371 | var o = idmap.GetComponent<global::MyComponent, TID>(id, gameObjectId); 4372 | o.enabled = enabled; 4373 | o.Material = idmap.GetObject<global::UnityEngine.Material>(Material); 4374 | o.Target = idmap.GetObject<global::UnityEngine.GameObject>(Target); 4375 | o.IntValue = IntValue; 4376 | //_PLACEHOLDER_FOR_DESERIALIZE_METHOD_BODY_DO_NOT_DELETE_OR_CHANGE_THIS_LINE_PLEASE 4377 | 4378 | return new ValueTask<object>(o); 4379 | } 4380 | } 4381 | } 4382 | 4383 | ``` 4384 | After successfully generating surrogates, you have the flexibility to make various customizations within your surrogate class. You can remove properties that you don't want to be serialized, add new properties, or perform other operations as needed. 4385 | 4386 | To prevent changes you make to surrogates from being tracked and displayed in the "Update Surrogates" window, you can set the enableUpdates attribute to false using the following syntax: 4387 | 4388 | ```C# 4389 | [Surrogate(typeof(global::MyComponent), _PROPERTY_INDEX, _TYPE_INDEX, enableUpdates: false)] 4390 | ``` 4391 | 4392 | For value types, make sure to set enabled to false like this: 4393 | 4394 | ```C# 4395 | [Surrogate(typeof(global::MyComponent), _PROPERTY_INDEX, _TYPE_INDEX, enabled: false)] 4396 | ``` 4397 | 4398 | Two constants, **int _PROPERTY_INDEX** and **int _TYPE_INDEX**, have following purpose: 4399 | 4400 | - int _PROPERTY_INDEX: This constant assists the surrogate updater in determining the index of the next property to be generated. 4401 | - int _TYPE_INDEX: This constant acts as a unique type index. 4402 | 4403 | **Please note that references to other types with surrogates are replaced with their identifier (TID). 4404 | You can use an "idmap" to generate unique IDs for objects and retrieve objects using their corresponding IDs.** 4405 | 4406 | <div style="page-break-after: always;"></div> 4407 | 4408 | ## Enumerators 4409 | The enumerator is created along with the surrogate. Enumerators are specialized classes used to retrieve Unity object dependencies in a structured manner. These enumerators enable the serialization of an entire object tree, ensuring that dependencies are deserialized before dependent objects during the deserialization process. 4410 | 4411 | ![Enumerators Folder][enumerators_folder] 4412 | 4413 | ```C# 4414 | namespace Battlehub.Storage.Enumerators 4415 | { 4416 | [ObjectEnumerator(typeof(global::MyComponent))] 4417 | public class MyComponentEnumerator : ObjectEnumerator<global::MyComponent> 4418 | { 4419 | public override bool MoveNext() 4420 | { 4421 | do 4422 | { 4423 | switch (Index) 4424 | { 4425 | 4426 | case 0: 4427 | if (MoveNext(TypedObject.Material, 5)) 4428 | return true; 4429 | break; 4430 | case 1: 4431 | if (MoveNext(TypedObject.Target, 6)) 4432 | return true; 4433 | break; 4434 | case 2: 4435 | if (MoveNext(Object, -1)) 4436 | return true; 4437 | break; 4438 | default: 4439 | return false; 4440 | } 4441 | } 4442 | while (true); 4443 | } 4444 | } 4445 | } 4446 | ``` 4447 | 4448 | **Note that the second parameter of the MoveNext method is the property index, which should be equal to the argument of the ProtoMember attribute assigned to that property in the surrogate class.** 4449 | 4450 | You can also use the following simplified syntax when editing an enumerator: 4451 | ```C# 4452 | namespace Battlehub.Storage.Enumerators 4453 | { 4454 | [ObjectEnumerator(typeof(global::MyComponent))] 4455 | public class MyComponentEnumerator : ObjectEnumerator<global::MyComponent> 4456 | { 4457 | protected override IEnumerator<(object Object, int Key)> GetNext() 4458 | { 4459 | yield return (TypedObject.Material, 5); 4460 | yield return (TypedObject.Target, 6); 4461 | yield return (TypedObject, -1); 4462 | } 4463 | } 4464 | } 4465 | ``` 4466 | 4467 | ## Dynamic Surrogates (Preview) 4468 | 4469 | It is possible to avoid creating surrogates by hand, instead letting the runtime asset database serialize types using reflection at runtime. This process is roughly equivalent to Unity serialization rules. It works with fields and not with properties. 4470 | 4471 | To enable Dynamic Surrogate for a type, use the following code: 4472 | 4473 | ```csharp 4474 | var assetDatabase = IOC.Resolve<IAssetDatabaseModel>(); 4475 | assetDatabase.AddRuntimeSerializableTypes(typeof(MyMonoBehaviour)); 4476 | ``` 4477 | 4478 | To use field serialization, ensure that the field: 4479 | 4480 | - Is public, or has a `SerializeField` attribute. 4481 | - Isn’t static. 4482 | - Isn’t const. 4483 | - Isn’t readonly. 4484 | - Has a field type that can be serialized: 4485 | - Primitive data types (int, float, double, bool, string, etc.) 4486 | - Enum types (32 bits or smaller) 4487 | - Fixed-size buffers 4488 | - Unity built-in types, for example, Vector2, Vector3, Rect, Matrix4x4, Color. *Some types like AnimationCurve will not be serialized.* 4489 | - Custom structs with the `Serializable` attribute 4490 | - References to objects that derive from `UnityEngine.Object` 4491 | - Custom classes with the `Serializable` attribute (see Serialization of custom classes). 4492 | - An array of a field type mentioned above. 4493 | - A `List<T>` of a field type mentioned above. 4494 | 4495 | ### Serialization of Custom Classes 4496 | 4497 | For Unity to serialize a custom class, ensure the class: 4498 | 4499 | - Has the `Serializable` attribute. 4500 | - Isn’t static. 4501 | 4502 | The `[SerializeReference]` attribute is not supported. 4503 | 4504 | ### Custom Serialization 4505 | 4506 | Sometimes you might want to serialize something that the serializer doesn’t support (for example, a C# Dictionary). The best approach is to implement the `ISerializationCallbackReceiver` interface in your class. This allows you to implement callbacks that are invoked at key points during serialization and deserialization: 4507 | 4508 | - When an object is about to be serialized, Runtime Asset Database invokes the `OnBeforeSerialize()` callback. Inside this callback is where you can transform your data into something Runtime Asset Database understands. For example, to serialize a C# Dictionary, copy the data from the Dictionary into an array of keys and an array of values. 4509 | - After the `OnBeforeSerialize()` callback is complete, Runtime Asset Database serializes the arrays. 4510 | - Later, when the object is deserialized, Runtime Asset Database invokes the `OnAfterDeserialize()` callback. Inside this callback is where you can transform the data back into a form that’s convenient for the object in memory. For example, use the key and value arrays to repopulate the C# Dictionary. 4511 | 4512 | 4513 | ## Build All 4514 | After finishing creating or updating surrogates, make sure to click **"Tools" > "Runtime Asset Database" > "Build All"** from the main menu. This command will build the type model and serializer. 4515 | 4516 | ![Build All Menu][build_all_2] 4517 | ![Type Model][build_all] 4518 | 4519 | ## Support 4520 | If you cannot find something in the documentation or have any questions, please feel free to send an email to [Battlehub@outlook.com](mailto:Battlehub@outlook.com) or ask directly in [this support group](https://t.me/battlehub). You can also create an issue [here](https://github.com/Battlehub0x/RTE_Docs/issues?q=is%3Aissue+is%3Aclosed). 4521 | Keep up the great work in your development journey! 😊 4522 | 4523 | <div style="page-break-after: always;"></div> 4524 | 4525 | [youtube_icon]: Docs/Images/overview.png 4526 | [getting_started_1]: Docs/Images/getting_started_1.png 4527 | [getting_started_2]: Docs/Images/getting_started_2.png 4528 | [getting_started_3]: Docs/Images/getting_started_3.jpg 4529 | [getting_started_4]: Docs/Images/getting_started_4.jpg 4530 | [getting_started_5]: Docs/Images/getting_started_5.jpg 4531 | [example_scenes_1]: Docs/Images/example_scenes_1.jpg 4532 | [example_scenes_2]: Docs/Images/example_scenes_2.jpg 4533 | [compatibility_mode]: Docs/Images/compatibility_mode.jpg 4534 | [universal_rp_1]: Docs/Images/universal_rp_1.jpg 4535 | [universal_rp_2]: Docs/Images/universal_rp_2.jpg 4536 | [universal_rp_3]: Docs/Images/universal_rp_3.jpg 4537 | [generate_surrogate]: Docs/Images/generate_surrogate.png 4538 | [surrogates_folder]: Docs/Images/surrogates_folder.png 4539 | [enumerators_folder]: Docs/Images/enumerators_folder.png 4540 | [build_all]: Docs/Images/build_all.png 4541 | [expose_to_editor]: Docs/Images/expose_to_editor.jpg 4542 | [use_zero_time_scale_in_edit_mode]: Docs/Images/use_zero_time_scale_in_edit_mode.jpg 4543 | [builtin_windows]: Docs/Images/builtin_windows.jpg 4544 | [main_menu]: Docs/Images/main_menu.jpg 4545 | [context_menu]: Docs/Images/context_menu.jpg 4546 | [window]: Docs/Images/window.jpg 4547 | [custom_window_1]: Docs/Images/custom_window_1.jpg 4548 | [custom_window_2]: Docs/Images/custom_window_2.jpg 4549 | [custom_window_3]: Docs/Images/custom_window_3.jpg 4550 | [extending_window_1]: Docs/Images/extending_window_1.jpg 4551 | [override_layout_1]: Docs/Images/override_layout_1.jpg 4552 | [override_layout_2]: Docs/Images/override_layout_2.jpg 4553 | [override_layout_3]: Docs/Images/override_layout_3.jpg 4554 | [override_scene_params]: Docs/Images/override_scene_params.jpg 4555 | [override_tools]: Docs/Images/override_tools.jpg 4556 | [ui_scale_1]: Docs/Images/ui-scale-before-override.png 4557 | [ui_scale_2]: Docs/Images/ui-scale-after-override.png 4558 | [override_theme]: Docs/Images/override_theme.jpg 4559 | [inspector]: Docs/Images/inspector.png 4560 | [inspector_editors]: Docs/Images/component_gameobject_material_editors.jpg 4561 | [inspector_property_editors]: Docs/Images/property_editors.jpg 4562 | [inspector_configuration_1]: Docs/Images/inspector_configuration_1.jpg 4563 | [inspector_configuration_window]: Docs/Images/inspector_configuration_window.jpg 4564 | [my_component]: Docs/Images/my_component.jpg 4565 | [build_all_2]: Docs/Images/build_all_2.jpg 4566 | [manage_projects_example]: Docs/Images/manage_projects_example.jpg 4567 | [manage_projects_example_2]: Docs/Images/manage_projects_example_2.jpg 4568 | [instantiate_asset_example]: Docs/Images/instantiate_asset_example.jpg 4569 | [hierarchy_root]:Docs/Images/hierarchy_root.jpg 4570 | [custom_external_asset_loader]:Docs/Images/custom_external_asset_loader.jpg 4571 | [asset_database_project_folder]:Docs/Images/assetdatabase_project_folder.jpg 4572 | [persistent_data_path]:Docs/Images/persistent_data_path.jpg 4573 | [trilib_loader]:Docs/Images/trilib_loader.jpg 4574 | [import_sources_1]:Docs/Images/import_sources_1.jpg 4575 | [import_sources_2]:Docs/Images/import_sources_2.jpg 4576 | [import_sources_3]:Docs/Images/import_sources_3.jpg 4577 | [import_sources_4]:Docs/Images/import_sources_4.jpg 4578 | [import_sources_5]:Docs/Images/import_sources_5.jpg 4579 | [modify_import_source_1]:Docs/Images/modify_importsource_1.jpg 4580 | [modify_import_source_2]:Docs/Images/modify_importsource_2.jpg 4581 | [modify_import_source_3]:Docs/Images/modify_importsource_3.jpg 4582 | [import_source_addressable_1]:Docs/Images/import_source_addressable_1.jpg 4583 | [import_source_addressable_2]:Docs/Images/import_source_addressable_2.jpg 4584 | [import_source_addressable_3]:Docs/Images/import_source_addressable_3.jpg 4585 | [my_import_source_1]:Docs/Images/my_import_source_1.jpg 4586 | [my_import_source_2]:Docs/Images/my_import_source_2.jpg 4587 | [file_importers_1]:Docs/Images/file_importers_1.jpg 4588 | [file_importers_2]:Docs/Images/file_importers_2.jpg 4589 | [jint_scripting_1]:Docs/Images/jint_scripting_1.jpg 4590 | [jint_scripting_2]:Docs/Images/jint_scripting_2.jpg 4591 | [jint_scripting_3]:Docs/Images/jint_scripting_3.jpg 4592 | [jint_scripting_4]:Docs/Images/jint_scripting_4.jpg 4593 | [jint_scripting_5]:Docs/Images/jint_scripting_5.jpg 4594 | [jint_scripting_6]:Docs/Images/jint_scripting_6.jpg 4595 | [jint_scripting_7]:Docs/Images/jint_scripting_7.jpg 4596 | [jint_scripting_8]:Docs/Images/jint_scripting_8.jpg 4597 | [jint_scripting_9]:Docs/Images/jint_scripting_9.jpg 4598 | 4599 | 4600 | 4601 | 4602 | --------------------------------------------------------------------------------