├── Docs
└── Images
│ ├── Youtube.png
│ ├── build_all.png
│ ├── create_asset_variant.png
│ ├── enumerators_folder.png
│ ├── generate_surrogate.png
│ ├── getting_started.png
│ ├── import_external_asset.png
│ ├── instantiate_asset.png
│ ├── register_external_asset.png
│ ├── runtme_asset_database.png
│ └── surrogates_folder.png
├── README.md
└── README.pdf
/Docs/Images/Youtube.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BattlehubCode/RuntimeAssetDatabase/57fb5e83b9fea4aeca2e5fd1fa32b8b5d51c44d6/Docs/Images/Youtube.png
--------------------------------------------------------------------------------
/Docs/Images/build_all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BattlehubCode/RuntimeAssetDatabase/57fb5e83b9fea4aeca2e5fd1fa32b8b5d51c44d6/Docs/Images/build_all.png
--------------------------------------------------------------------------------
/Docs/Images/create_asset_variant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BattlehubCode/RuntimeAssetDatabase/57fb5e83b9fea4aeca2e5fd1fa32b8b5d51c44d6/Docs/Images/create_asset_variant.png
--------------------------------------------------------------------------------
/Docs/Images/enumerators_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BattlehubCode/RuntimeAssetDatabase/57fb5e83b9fea4aeca2e5fd1fa32b8b5d51c44d6/Docs/Images/enumerators_folder.png
--------------------------------------------------------------------------------
/Docs/Images/generate_surrogate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BattlehubCode/RuntimeAssetDatabase/57fb5e83b9fea4aeca2e5fd1fa32b8b5d51c44d6/Docs/Images/generate_surrogate.png
--------------------------------------------------------------------------------
/Docs/Images/getting_started.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BattlehubCode/RuntimeAssetDatabase/57fb5e83b9fea4aeca2e5fd1fa32b8b5d51c44d6/Docs/Images/getting_started.png
--------------------------------------------------------------------------------
/Docs/Images/import_external_asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BattlehubCode/RuntimeAssetDatabase/57fb5e83b9fea4aeca2e5fd1fa32b8b5d51c44d6/Docs/Images/import_external_asset.png
--------------------------------------------------------------------------------
/Docs/Images/instantiate_asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BattlehubCode/RuntimeAssetDatabase/57fb5e83b9fea4aeca2e5fd1fa32b8b5d51c44d6/Docs/Images/instantiate_asset.png
--------------------------------------------------------------------------------
/Docs/Images/register_external_asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BattlehubCode/RuntimeAssetDatabase/57fb5e83b9fea4aeca2e5fd1fa32b8b5d51c44d6/Docs/Images/register_external_asset.png
--------------------------------------------------------------------------------
/Docs/Images/runtme_asset_database.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BattlehubCode/RuntimeAssetDatabase/57fb5e83b9fea4aeca2e5fd1fa32b8b5d51c44d6/Docs/Images/runtme_asset_database.png
--------------------------------------------------------------------------------
/Docs/Images/surrogates_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BattlehubCode/RuntimeAssetDatabase/57fb5e83b9fea4aeca2e5fd1fa32b8b5d51c44d6/Docs/Images/surrogates_folder.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Runtime Asset Database for Unity
2 |
3 | The [**Runtime Asset Database**](https://assetstore.unity.com/packages/tools/modeling/runtime-asset-database-263289) is a library designed to simplify the implementation of a runtime save and load subsystem in your Unity application. This library replicates and extends the familiar concepts of prefabs, prefab variants, and assets found within the Unity Editor, making it easier than ever to manage and manipulate game assets at runtime and implement workflows similar to those of the Unity Editor dynamically during runtime.
4 |
5 | [![Promo Video][youtube_icon]](https://www.youtube.com/watch?v=DXWriLgrWdE)
6 |
7 | > **Note**
8 | The repository containing the project used to create the above video can be found [here](https://github.com/Battlehub0x/RuntimeAssetDatabaseGameKit)
9 |
10 | > **Code Companion**
11 | https://chat.openai.com/g/g-1UCDubUwr-your-code-companion-don-t-trust-me-blindly
12 |
13 |
14 | ## Introduction
15 |
16 | Unity developers often rely on the convenience and flexibility of the Editor's asset management system when designing their games. However, when it comes to implementing a save and load system at runtime, this process can become more complex. The Runtime Asset Database bridges this gap by bringing the essential asset management functionalities you're accustomed to into the runtime environment.
17 |
18 | ## Features
19 | - **Runtime Asset Management API:** Provides functionality to create, load, and manage assets during runtime.
20 | - **Built on Unity Editor Prefab Concepts:** Utilizes familiar concepts from the Unity Editor's prefab workflow.
21 | - **Asset and Asset Variant Support:** Supports assets and their variants.
22 | - **Extensibility with new types and components:** Allows for the extension with new serializable types.
23 | - **Pluggable External Asset Importers:** Offers the ability to integrate external asset importers seamlessly.
24 |
25 |
26 |
27 | ## Getting Started
28 | 1. Unpack **StarterKit** Unity Package
29 | 2. Click Tools > Runtime Asset Database > **Build All**
30 | 3. Click Tools > Runtime Asset Database > **Create Host**
31 | 4. Create a new C# script named **GettingStarted.cs** in your Unity project.
32 | 5. Add the Following Code to Your Script:
33 | ```C#
34 | using UnityEngine;
35 | using Battlehub.Storage;
36 |
37 | public class GettingStarted : MonoBehaviour
38 | {
39 | private IAssetDatabase m_assetDatabase;
40 |
41 | async void Start()
42 | {
43 | // Define your project path
44 | string projectPath = $"MyProject";
45 |
46 | // Obtain a reference to the asset database
47 | IAssetDatabase m_assetDatabase = RuntimeAssetDatabase.Instance;
48 |
49 | // Load the project
50 | await m_assetDatabase.LoadProjectAsync(projectPath);
51 | }
52 | }
53 | ```
54 |
55 |
56 |
57 | 6. Modify Your Script as Follows:
58 |
59 | ```C#
60 | using UnityEngine;
61 | using Battlehub.Storage;
62 |
63 | public class GettingStarted : MonoBehaviour
64 | {
65 | async void Start()
66 | {
67 | string projectPath = $"MyProject"; // Define your project path
68 |
69 | // Obtain a reference to the asset database
70 | IAssetDatabase m_assetDatabase = RuntimeAssetDatabase.Instance;
71 |
72 | // Load the project
73 | await m_assetDatabase.LoadProjectAsync(projectPath);
74 |
75 | // Delete the "Assets" folder if it exists
76 | if (m_assetDatabase.Exists("Assets"))
77 | await m_assetDatabase.DeleteFolderAsync("Assets");
78 |
79 | // Create a new "Assets" folder
80 | await m_assetDatabase.CreateFolderAsync("Assets");
81 |
82 | // Create a primitive object (capsule) and make some modifications
83 | var go = GameObject.CreatePrimitive(PrimitiveType.Capsule);
84 | var filter = go.GetComponent();
85 | var renderer = go.GetComponent();
86 | var mesh = filter.mesh;
87 | var material = renderer.material;
88 | material.color = new Color32(0x0, 0x74, 0xFF, 0x0);
89 |
90 | // Create a mesh asset
91 | await m_assetDatabase.CreateAssetAsync(mesh, "Assets/Mesh.asset");
92 |
93 | // Create a material asset
94 | await m_assetDatabase.CreateAssetAsync(material, "Assets/Material.asset");
95 |
96 | // Create a "prefab" asset
97 | await m_assetDatabase.CreateAssetAsync(go, "Assets/Capsule.prefab");
98 |
99 | // Unload the project and destroy all assets to free up memory
100 | await m_assetDatabase.UnloadProjectAsync(destroy: true);
101 |
102 | // Load the project again
103 | await m_assetDatabase.LoadProjectAsync(projectPath);
104 |
105 | // Instantiate the prefab.
106 | await m_assetDatabase.InstantiateAssetAsync("Assets/Capsule.prefab");
107 | }
108 | }
109 | ```
110 | 7. Press the "Play" button in Unity. **You should now see an instance of the object loaded from the Runtime Asset Database in your Unity scene**
111 |
112 | ![Getting Started Result][getting_started_result]
113 |
114 |
115 |
116 | ## Definitions
117 | ### Folder
118 | A folder simply refers to a directory on disk within the Runtime Asset Database project directory.
119 |
120 | ### Asset
121 | An asset is any object derived from UnityEngine.Object that can be serialized and deserialized. It is represented by three files on disk: the meta file, data file, and thumbnail. Assets fall into two categories:
122 | - **Instantiable Assets**: These assets are analogous to prefabs in the Unity Editor. They can be instantiated and used directly in your project.
123 | - **Non-instantiable Assets**: Examples of non-instantiable assets include materials and meshes.
124 |
125 | Additionally, there is the concept of a Root Asset and a regular Asset.
126 | - **Root Asset**: A GameObject is an example of a Root Asset.
127 | - **Asset**: Components or meshes are examples of regular Assets. Their data is embedded in the same data file as the Root Asset.
128 |
129 | ### Asset Variant
130 | An **Asset Variant** is the equivalent of a Prefab Variant in the Unity Editor. It can only be created from an **Instantiable Asset**.
131 | Asset Variants become valuable when you need to define a set of predetermined variations of an Asset.
132 |
133 | ### External Asset
134 | An External Asset is an asset imported into the project using a specific importer, such as Addressable importers, importer that load asset from the Resources folder, GLB importer, or any other third-party importer.
135 | 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.
136 |
137 | ### Instance
138 | You can only instantiate an Asset, Asset Variant, or External Asset. The runtime asset database maintains mappings between instance parts and their corresponding asset parts
139 |
140 | ### Dirty Instance
141 | A "dirty" instance is one that has been marked to notify the runtime asset database that a change has occurred within an instance of an Asset Variant.
142 | This change needs to be saved to disk. When you load the Asset Variant next time, the asset database will read and apply this change to the base asset
143 |
144 | ### Detached Instance
145 | A detached instance is an instance that has no connection to the actual asset it originated from.
146 | You can convert an existing instance into a detached instance using the DetachAsync method, which will be discussed in more detail below.
147 |
148 | ### Meta File
149 | The Meta File contains asset metadata, which includes identifiers of dependencies, the asset's name, file ID. To get the metafile path, combine the file ID with the **.meta** extension; to get the thumbnail path, combine the file ID with the **.thumb** extension.
150 |
151 | ### Data File
152 | The Data File contains the binary serialized data of the asset. Runtime Asset Database uses protobuf-net as serializer.
153 |
154 | ### Thumbnail File
155 | The Thumbnail File contains image data of the asset's thumbnail texture.
156 |
157 | ### Surrogate
158 | Surrogates are classes with which the Serializer works. They act as intermediary classes that facilitate the reading and writing of data to the target Unity object.
159 | While these classes are often auto-generated, you have the flexibility to edit or create them from scratch.
160 |
161 | ### Enumerator
162 | Enumerators are classes used to retrieve Unity object dependencies in a structured manner.
163 | They enable the serialization of an entire object tree in a way that ensures dependencies are deserialized before dependent objects during the deserialization process.
164 | Similar to surrogates, enumerators are often auto-generated, but users also have the flexibility to create or edit them.
165 |
166 |
167 |
168 | ## Examples
169 | ### Load project
170 | ```C#
171 | using System;
172 | using UnityEngine;
173 | namespace Battlehub.Storage.Samples
174 | {
175 | public class LoadProjectExample : MonoBehaviour
176 | {
177 | private IAssetDatabase m_assetDatabase;
178 |
179 | private async void Start()
180 | {
181 | m_assetDatabase = RuntimeAssetDatabase.Instance;
182 |
183 | string fullpath = $"{Application.persistentDataPath}/Example Project";
184 |
185 | // load the project (creates a project folder if it does not exist)
186 | await m_assetDatabase.LoadProjectAsync(fullpath);
187 |
188 | // get root folder id
189 | Guid rootID = m_assetDatabase.RootID;
190 |
191 | // get child id by root id
192 | foreach (var childID in m_assetDatabase.GetChildren(rootID, sortByName: true))
193 | {
194 | // get asset metadata by id
195 | var meta = m_assetDatabase.GetMeta(childID);
196 |
197 | if (m_assetDatabase.IsFolder(childID))
198 | {
199 | Debug.Log($"Folder {meta.Name} {meta.FileID}");
200 | }
201 | else
202 | {
203 | Debug.Log($"{meta.Name} {meta.FileID}");
204 | }
205 | }
206 | }
207 | }
208 | }
209 | ```
210 |
211 |
212 |
213 | ### Unload project
214 | ```C#
215 | using UnityEngine;
216 | namespace Battlehub.Storage.Samples
217 | {
218 | public class UnloadProjectExample : MonoBehaviour
219 | {
220 | private IAssetDatabase m_assetDatabase;
221 |
222 | private async void Start()
223 | {
224 | m_assetDatabase = RuntimeAssetDatabase.Instance;
225 |
226 | string projectPath = $"{Application.persistentDataPath}/Example Project";
227 |
228 | await m_assetDatabase.LoadProjectAsync(projectPath);
229 | }
230 |
231 | private async void OnDestroy()
232 | {
233 | if (m_assetDatabase != null)
234 | {
235 | if (m_assetDatabase.IsProjectLoaded)
236 | {
237 | // unload the project and all assets
238 |
239 | // destroy: true -> destroy the corresponding objects and game objects
240 |
241 | await m_assetDatabase.UnloadProjectAsync(destroy: true);
242 | }
243 | }
244 | }
245 | }
246 | }
247 | ```
248 |
249 |
250 |
251 | ### Import external asset
252 | ```C#
253 | using UnityEngine;
254 | namespace Battlehub.Storage.Samples
255 | {
256 | ///
257 | /// ---------------------------------------------------------------------------
258 | /// First register an external asset loader. This should only be done once,
259 | /// after that you can import multiple asses using this loader.
260 | /// ---------------------------------------------------------------------------
261 | /// In this example, I'm using the built-in ResourcesLoader for simplicity,
262 | /// but it could be any loader which implements the IExternalAssetLoader
263 | /// interface (AddressablesLoader, glTFLoader, FBXLoader, etc.)
264 | /// ---------------------------------------------------------------------------
265 | /// The loader in this example loads an asset from the Resources folder.
266 | /// In this particular example, the asset with the key "Hellephant" is in
267 | /// Assets/Battlehub/Storage.Samples.ProjectBrowser/Content/Resources
268 | /// ---------------------------------------------------------------------------
269 | ///
270 | public class ImportExternalAssetExample : MonoBehaviour
271 | {
272 | private IAssetDatabase m_assetDatabase;
273 |
274 | private async void Start()
275 | {
276 | m_assetDatabase = RuntimeAssetDatabase.Instance;
277 |
278 | string projectPath = $"{Application.persistentDataPath}/Example Project";
279 | await m_assetDatabase.LoadProjectAsync(projectPath);
280 |
281 | var rootID = m_assetDatabase.RootID;
282 | string key = "Hellephant";
283 | string loaderID = nameof(ResourcesLoader);
284 |
285 | IExternalAssetLoader loader = new ResourcesLoader();
286 | await m_assetDatabase.RegisterExternalAssetLoaderAsync(loaderID, loader);
287 |
288 | // convert externalAssetKey to unique file id
289 | var targetFileID = m_assetDatabase.GetUniqueFileID(rootID, $"{key}");
290 |
291 | // import external asset
292 | await m_assetDatabase.ImportExternalAssetAsync(key, loaderID, targetFileID);
293 |
294 | // instantiate imported asset
295 | await m_assetDatabase.InstantiateAssetAsync(targetFileID);
296 | }
297 | }
298 | }
299 | ```
300 | ![Import External Asset Result][import_external_asset]
301 |
302 | > **Note**
303 | To use the AddressablesLoader, make sure to import the [Addressables package](https://docs.unity3d.com/Packages/com.unity.addressables@1.18/manual/index.html)
304 |
305 | > **Note**
306 | You can also create your own external asset loader. To do this, create a new class and implement the IExternalAssetLoader interface:
307 |
308 | ```C#
309 | using System;
310 | using System.Threading.Tasks;
311 | using UnityEngine;
312 | namespace Battlehub.Storage.Samples
313 | {
314 | public class MyLoader : IExternalAssetLoader
315 | {
316 | public Task