├── Hydrogen.Entities.Hybrid
├── IConvertScriptableObjectToBlob.cs.meta
├── ScriptableObjectConversionSystem.cs.meta
├── Hydrogen.Entities.Hybrid.asmdef.meta
├── Hydrogen.Entities.Hybrid.asmdef
├── IConvertScriptableObjectToBlob.cs
└── ScriptableObjectConversionSystem.cs
├── README.md
├── Hydrogen.Entities.Hybrid.Tests
├── Assembly.cs
├── Content.meta
├── Hydrogen.Entities.Hybrid.Tests.asmdef.meta
├── Assembly.cs.meta
├── ConvertScriptableObjectToBlobTests.cs.meta
├── Hydrogen.Entities.Hybrid.Tests.asmdef
└── ConvertScriptableObjectToBlobTests.cs
├── Documentation~
├── TableOfContents.md
├── manual.md
├── index.md
└── scriptableobject-conversion-system.md
├── CHANGELOG.md.meta
├── LICENSE.md.meta
├── README.md.meta
├── package.json.meta
├── Samples~
└── ConfigFileLoading
│ ├── README.md.meta
│ ├── Scripts.meta
│ ├── Contents.meta
│ ├── Contents
│ ├── ConfigFileLoading.unity.meta
│ └── ConfigFileLoading.unity
│ ├── Scripts
│ ├── IniFileExample.cs.meta
│ └── IniFileExample.cs
│ └── README.md
├── Hydrogen.Entities.Hybrid.meta
├── Hydrogen.Entities.Hybrid.Tests.meta
├── .gitignore
├── LICENSE.md
├── package.json
└── CHANGELOG.md
/Hydrogen.Entities.Hybrid/IConvertScriptableObjectToBlob.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a47df094a25347a7a212c8ce3af2ad06
3 | timeCreated: 1563759840
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid/ScriptableObjectConversionSystem.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 78ff4fc2117d41acadecd86568e68fe6
3 | timeCreated: 1559771196
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Hydrogen.Entities
2 |
3 | A collection of helpers for working with Unity's ECS framework, particularly in setup and configuration authoring.
4 |
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid.Tests/Assembly.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("Hydrogen.Entities.Hybrid.Editor.Tests")]
--------------------------------------------------------------------------------
/Documentation~/TableOfContents.md:
--------------------------------------------------------------------------------
1 | * [Hydrogen.Entities overview](./index.md)
2 | * [Manual](./manual.md)
3 | * [ScriptableObjectConversionSystem](./scriptableobject-conversion-system.md)
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d2e05bd62bce0c64bb29d261fbd2f519
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/LICENSE.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ce6cf71b8a235bb40a6d70c96f455bca
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 97f35be212c3095409434003d55730aa
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/package.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7b8f06830791a274691969c3f9deac77
3 | PackageManifestImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Samples~/ConfigFileLoading/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 59e56f2411362bf48b2ab4ed1fdc560a
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 757dcd622743f3440a093973156c8719
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid.Tests.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 84b2c54f3714af54b8dbeb6c96db8a85
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Samples~/ConfigFileLoading/Scripts.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 379543cd9c3636d4da12fda904e719b4
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid.Tests/Content.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 72d0f23944962c140acfde531be8eb7d
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Samples~/ConfigFileLoading/Contents.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 24972df7c033c6a44be48d093c79802f
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Samples~/ConfigFileLoading/Contents/ConfigFileLoading.unity.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 378a0283481a92c42af83964cb124948
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid/Hydrogen.Entities.Hybrid.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 44ab9bb22c816394bb1625d8e2ca2fd6
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid.Tests/Hydrogen.Entities.Hybrid.Tests.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 073cb90ce0e3ee34caae66aba0367551
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid.Tests/Assembly.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0da56aab8fa4e984993503a60b06e271
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Samples~/ConfigFileLoading/Scripts/IniFileExample.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5feedfb56761f0c4ab76171409ced649
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid.Tests/ConvertScriptableObjectToBlobTests.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e6a015b30f0777a459acc3a0a6bf2c67
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | [Ll]ibrary/
2 | [Tt]emp/
3 | [Oo]bj/
4 | [Bb]uild/
5 | [Bb]uilds/
6 | Assets/AssetStoreTools*
7 |
8 | # Visual Studio cache directory
9 | .vs/
10 |
11 | # Autogenerated VS/MD/Consulo solution and project files
12 | ExportedObj/
13 | .consulo/
14 | *.csproj
15 | *.unityproj
16 | *.sln
17 | *.suo
18 | *.tmp
19 | *.user
20 | *.userprefs
21 | *.pidb
22 | *.booproj
23 | *.svd
24 | *.pdb
25 | *.opendb
26 |
27 | # Unity3D generated meta files
28 | *.pidb.meta
29 | *.pdb.meta
30 |
31 | # Unity3D Generated File On Crash Reports
32 | sysinfo.txt
33 |
34 | # Builds
35 | *.apk
36 | *.unitypackage
37 |
--------------------------------------------------------------------------------
/Documentation~/manual.md:
--------------------------------------------------------------------------------
1 | # Hydrogen.Entities manual
2 |
3 | This is the manual for the Hydrogen.Entities DOTS Utility framework.
4 |
5 | ## Introduction
6 |
7 | * [Hydrogen.Entities overview](./index.md)
8 |
9 | ## **Getting Started**
10 |
11 | * [Installation](./index.md#installation)
12 | * [Using the ScriptableObjectConversionSystem](./scriptableobject-conversion-system.md)
13 | * [Implementing IConvertScriptableObjectToBlob<T>](./scriptableobject-conversion-system.md#iconvertscriptableobjecttoblobt)
14 | * [Providing custom functions for externally defined ScriptableObjects](./scriptableobject-conversion-system.md#scripttoblobfuncin-t0-t1)
15 |
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid/Hydrogen.Entities.Hybrid.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Hydrogen.Entities.Hybrid",
3 | "rootNamespace": "",
4 | "references": [
5 | "GUID:734d92eba21c94caba915361bd5ac177",
6 | "GUID:8819f35a0fc84499b990e90a4ca1911f",
7 | "GUID:e0cd26848372d4e5c891c569017e11f1",
8 | "GUID:5f3cf485eb0554709a8abbeace890c86"
9 | ],
10 | "includePlatforms": [],
11 | "excludePlatforms": [],
12 | "allowUnsafeCode": true,
13 | "overrideReferences": false,
14 | "precompiledReferences": [],
15 | "autoReferenced": true,
16 | "defineConstraints": [],
17 | "versionDefines": [],
18 | "noEngineReferences": false
19 | }
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid.Tests/Hydrogen.Entities.Hybrid.Tests.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Hydrogen.Entities.Hybrid.Tests",
3 | "rootNamespace": "",
4 | "references": [
5 | "GUID:734d92eba21c94caba915361bd5ac177",
6 | "GUID:8e2f79f713f5a47dcb69903037d4bc0e",
7 | "GUID:8819f35a0fc84499b990e90a4ca1911f",
8 | "GUID:bd9484a338da1458bb1d27afe56e6f90",
9 | "GUID:27619889b8ba8c24980f49ee34dbb44a",
10 | "GUID:0acc523941302664db1f4e527237feb3",
11 | "GUID:44ab9bb22c816394bb1625d8e2ca2fd6"
12 | ],
13 | "includePlatforms": [],
14 | "excludePlatforms": [],
15 | "allowUnsafeCode": false,
16 | "overrideReferences": true,
17 | "precompiledReferences": [
18 | "nunit.framework.dll"
19 | ],
20 | "autoReferenced": false,
21 | "defineConstraints": [
22 | "UNITY_INCLUDE_TESTS"
23 | ],
24 | "versionDefines": [],
25 | "noEngineReferences": false
26 | }
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid/IConvertScriptableObjectToBlob.cs:
--------------------------------------------------------------------------------
1 | using Unity.Entities;
2 |
3 | namespace Hydrogen.Entities
4 | {
5 | ///
6 | /// Interface that defines a contract for converting a to a .
7 | ///
8 | /// The struct type our Blob Asset Reference will point to.
9 | public interface IConvertScriptableObjectToBlob
10 | where T0 : struct
11 | {
12 | ///
13 | /// Defines the Conversion function to convert this to a .
14 | ///
15 | /// The doing the converting. Allows us to build reference chains to other converted blobs.
16 | /// The constructed
17 | BlobAssetReference Convert(ScriptableObjectConversionSystem conversion);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Thomas Key
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Samples~/ConfigFileLoading/README.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | This sample demonstrates a simple mapping of ini properties to a blob asset,
4 | and usage of the conversion framework without matching Authoring/SO components.
5 |
6 | # The MonoBehaviour Components
7 |
8 | ## IniFileExample
9 |
10 | This script parses the provided IniContents string field, and sets the "file" name for the ini file data.
11 | It then matches the fields from the file to the [IniFile](#inifile) described below.
12 |
13 | # The DOTS Components
14 |
15 | ## IniFile
16 |
17 | This struct is the Blob Asset and contains the corresponding fields.
18 | - Name (BlobString) - the name of the ini file.
19 | - Bar (BlobString)
20 | - Foo (int)
21 | - Baz (float)
22 | - Qux (Speed) - uses the Speed Enum described below.
23 |
24 | ## Speed
25 | An enum to demonstrate enum parsing and conversion has 3 values.
26 | - Slow
27 | - Medium
28 | - Fast
29 |
30 | # The DOTS Systems
31 |
32 | ## IniFileConvertSystem
33 |
34 | Derives from ```SingletonBlobConvertSystem``` to handle the boilerplate for us.
35 |
36 | ## IniFileChangedSystem
37 |
38 | Derives from ```SingletonBlobChangedComponentSystem``` to report the converted singleton data and verify success.
39 | Handles the boilerplate for us.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.periodyc.hydrogen.entities",
3 | "version": "0.2.2-preview.1",
4 | "displayName": "Hydrogen Entities", "description": "A collection of helpers for working with Unity's ECS framework.",
5 | "unity": "2019.3",
6 | "dependencies": {
7 | "com.unity.entities": "0.3.0-preview.4",
8 | "com.unity.collections": "0.3.0-preview.0",
9 | "com.unity.jobs": "0.2.1-preview.3",
10 | "com.unity.test-framework": "1.1.3"
11 | },
12 | "samples": [
13 | {
14 | "displayName": "Simple World Converters",
15 | "description": "Shows how to implement and use the basic Converters.",
16 | "path": "Samples~/SimpleWorldConverter"
17 | },
18 | {
19 | "displayName": "Config File Loading",
20 | "description": "Shows how to load an ini file from disk and store it in a blob singleton.",
21 | "path": "Samples~/ConfigFileLoading"
22 | },
23 | {
24 | "displayName": "Simple Database Example",
25 | "description": "Shows how to implement a simple Key-value database with Blob Assets and NativeHashMap.",
26 | "path": "Samples~/SimpleDatabaseExample"
27 | }
28 | ],
29 | "keywords": [
30 | "entities",
31 | "utility"
32 | ],
33 | "author": {
34 | "name": "Thomas Key",
35 | "email": "thomas.key@periodyc.com",
36 | "url": "https://www.periodyc.com"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Documentation~/index.md:
--------------------------------------------------------------------------------
1 | # About Hydrogen Entities
2 |
3 | Hydrogen.Entities is a support library that contains several utilities for working with [Unity's DOTS framework](https://unity.com/dots).
4 | It is designed to be as simple as possible to assist in certain types of [Entity conversions](https://docs.unity3d.com/Packages/com.unity.entities@0.3/api/Unity.Entities.Entity.html).
5 |
6 | In particular it has support for creating singleton data for configuration purposes and creating [```BlobAssetReference```](https://docs.unity3d.com/Packages/com.unity.entities@0.3/api/Unity.Entities.BlobAssetReference-1.html) from ScriptableObjects.
7 | This makes it easier to use Pure(er) ECS data and less need for overhead when accessing shared configuration data.
8 |
9 | There are also [Unit Tests](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/index.html) setup to ensure robustness, and to serve as examples for programmers interested in utilizing the framework.
10 |
11 | # Installation
12 |
13 | To install this package, you can follow the instructions for using [github packages](https://docs.unity3d.com/Manual/upm-git.html) from the [Unity Package Manager](https://docs.unity3d.com/Packages/com.unity.package-manager-ui@latest/index.html).
14 | > **Note**: The github page for the framework is https://github.com/periodyctom/Hydrogen.Entities.
15 |
16 | # Using Hydrogen.Entities
17 |
18 | You can read the [manual](./manual.md) for usage information.
19 |
20 | The [table of contents](./TableOfContents.md) has a listing of the important pages.
21 |
22 | # Technical Details
23 |
24 | ## Requirements
25 |
26 | This version of Hydrogen.Entities has been tested with 2019.3b4+.
27 |
28 | ## Document History
29 | | Date | Reason |
30 | | :--------- | :----------------------------------- |
31 | | December 29, 2020 | Long overdue cleanup after getting rid of code rot. Several Unity DOTS features now go beyond what this API supports. |
32 | | December 03, 2019 | Updated after documentation improvements and hyperlink updates. |
33 | | October 14, 2019 | Updated after major revision to 0.2.0 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [0.2.2-preview.2] - 2020-02-17
4 | - Minor fix for an API rename
5 |
6 | ## [0.2.2-preview.1] - 2019-11-04
7 | - Removed unnecessary system ordering attributes from sample systems, thanks to the system group additions.
8 |
9 | ## [0.2.2-preview.0] - 2019-11-03
10 | - Added SingletonConvertGroup and SingletonPostConvertGroup to simplify managing order for conversion systems.
11 | - SingletonConvertSystem now all by default run in SingletonConvertGroup
12 | - All SingletonChanged* variant systems now run in SingletonPostConvertGroup
13 | - Bumped required Entities version to 0.3.0-preview.0, since it fixes several ConvertToEntity issues.
14 |
15 | ## [0.2.1-preview.1] - 2019-11-30
16 | - Fixed Warnings with using manual GO conversion in tests due to API changes.
17 |
18 | ## [0.2.1-preview.0] - 2019-11-27
19 | - Fixed issues with Entities 0.2.0 compatibility
20 | - Updated tests to fix issues with bugs being introduced and fixed in the API.
21 |
22 | ## [0.2.0-preview.3] - 2019-11-18
23 | - Added 3 working samples based on user feedback.
24 | - Added more base classes to reduce boilerplate for common system patterns.
25 | - Eliminated need to override a copy function for changing out Blob Asset Singletons.
26 |
27 | ## [0.2.0-preview.2] - 2019-11-05
28 | - Fixed Hydrogen.Entities and Hydrogen.Entities.Hybrid asmdefs not being auto-referenced for projects not using asmdefs.
29 | - Added Basic Converter Samples.
30 |
31 | ## [0.2.0-preview.1] - 2019-10-17
32 | - Removed Obsolete parts of the API
33 |
34 | ## [0.2.0-preview.0] - 2019-10-17
35 | - Obsoleted old Config and Singleton API, in favor of the new SingletonConverter Framework
36 | - Added full unit Test coverage for both ScriptableObjectConversionSystem, and the new SingletonConverter Framework.
37 | - Split things up into more assembly definitions.
38 | - Blob Singletons loaded via a converter in a subscene will now copy instead of assign to avoid invalid memory access
39 | errors
40 | - Rewrote Documentation
41 |
42 | ## [0.1.3] - 2019-09-05
43 | - Fixed Incorrect asmdef setting
44 | - Began work on new Conversion Framework.
45 |
46 | ## [0.1.2] - 2019-08-19
47 | - Simplified IConvertScriptableObjectToBlob API surface.
48 | - User can now access the GameObjectConversionSystem from ScriptableObjectConversionSystem's GoConversionSystem property.
49 |
50 | ## [0.1.1]
51 |
52 | Minor adjustments to package naming.
53 | Added Initial Documentation
54 |
55 | ## [0.1.0]
56 | Initial commits.
57 |
58 | Config and BlobAsset/ref helpers.
59 |
--------------------------------------------------------------------------------
/Samples~/ConfigFileLoading/Scripts/IniFileExample.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using Hydrogen.Entities;
5 | using Unity.Collections;
6 | using Unity.Entities;
7 | using UnityEngine;
8 | using UnityEngine.Assertions;
9 |
10 | [assembly: RegisterGenericComponentType(typeof(BlobRefData))]
11 | [assembly: RegisterGenericComponentType(typeof(SingletonConverter>))]
12 |
13 | namespace Hydrogen.Entities
14 | {
15 | public class IniFileExample : MonoBehaviour
16 | {
17 | private const string kFileName = "TestConfig.ini";
18 | private const string kIniContents = "foo=42\nbar=\"hello world\"\nbaz=12.34\nqux=Slow\n";
19 |
20 | [SerializeField] private string m_fileName = kFileName;
21 | [SerializeField, Multiline(8)] private string m_iniContents = kIniContents;
22 |
23 | private void Start()
24 | {
25 | using (var reader = new StringReader(m_iniContents))
26 | {
27 | var builder = new BlobBuilder(Allocator.Temp);
28 | BlobAssetReference configReference;
29 |
30 | try
31 | {
32 | ref IniFile root = ref builder.ConstructRoot();
33 |
34 | builder.AllocateString(ref root.Name, m_fileName);
35 |
36 | string line;
37 | while ((line = reader.ReadLine()) != null)
38 | {
39 | string[] pairStr = line.Split('=');
40 | if(pairStr.Length != 2 || string.IsNullOrEmpty(pairStr[0])) continue;
41 |
42 | switch (pairStr[0])
43 | {
44 | case "foo":
45 | int.TryParse(pairStr[1], out root.Foo);
46 | break;
47 | case "bar":
48 | if (root.Bar.Length != 0) continue;
49 | builder.AllocateString(ref root.Bar, pairStr[1]);
50 | break;
51 | case "baz":
52 | float.TryParse(pairStr[1], out root.Baz);
53 | break;
54 | case "qux":
55 | Enum.TryParse(pairStr[1], true, out root.Qux);
56 | break;
57 | }
58 | }
59 |
60 | configReference = builder.CreateBlobAssetReference(Allocator.Persistent);
61 | }
62 | finally
63 | {
64 | builder.Dispose();
65 | }
66 |
67 | Assert.IsTrue(configReference.IsCreated);
68 |
69 | BlobRefData blobRef = configReference;
70 | SingletonConverter> converter = blobRef;
71 |
72 | EntityManager entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
73 | Entity entity = entityManager.CreateEntity(typeof(SingletonConverter>));
74 | entityManager.SetComponentData(entity, converter);
75 | }
76 | }
77 | }
78 |
79 | public sealed class IniFileConvertSystem : SingletonBlobConvertSystem { }
80 |
81 | public sealed class IniFileChangedSystem : SingletonBlobChangedComponentSystem
82 | {
83 | protected override void OnUpdate()
84 | {
85 | ref IniFile iniFile = ref GetSingleton>().Resolve;
86 |
87 | var strBuilder = new StringBuilder(1024);
88 | strBuilder.AppendLine($"Ini File Name: {iniFile.Name.ToString()}");
89 | strBuilder.AppendLine($"Foo: {iniFile.Foo:D}");
90 | strBuilder.AppendLine($"Bar: {iniFile.Bar.ToString()}");
91 | strBuilder.AppendLine($"Baz: {iniFile.Baz:N}");
92 | strBuilder.AppendLine($"Qux Speed is: {iniFile.Qux.ToString()}");
93 |
94 | Debug.Log(strBuilder.ToString());
95 | }
96 | }
97 |
98 | public struct IniFile
99 | {
100 | public BlobString Name;
101 | public BlobString Bar;
102 | public int Foo;
103 | public float Baz;
104 | public Speed Qux;
105 | }
106 |
107 | public enum Speed
108 | {
109 | Slow,
110 | Medium,
111 | Fast
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Documentation~/scriptableobject-conversion-system.md:
--------------------------------------------------------------------------------
1 | # **ScriptableObjectConversionSystem**
2 |
3 | Similar to and piggybacking on the [GameObject](https://docs.unity3d.com/ScriptReference/GameObject.html) conversion pipeline.
4 | The ScriptableObjectConversionSystems has functions for creating Blob assets and [BlobAssetReference<T>](https://docs.unity3d.com/Packages/com.unity.entities@0.3/api/Unity.Entities.BlobAssetReference-1.html) to them from [ScriptableObjects](https://docs.unity3d.com/ScriptReference/ScriptableObject.html).
5 | The conversion system caches created BlobAssetReferences<T> so given the same inputs, you should get the same result.
6 | This allows a converted SO to be referenced in more than one place.
7 |
8 | The conversion flow is also given access to the GameObjectConversionSystem used in converting GOs, so you can create SO conversions that reference converted prefabs, given the originating prefab references!
9 |
10 | There are 2 main conversion routes:
11 |
12 | ## **IConvertScriptableObjectToBlob<T>**
13 | An interface that should be used on a ScriptableObject that defines it's own conversion into a Blob Asset.
14 | This is used to create a **BlobAssetReference<T>** that points to the converted SO blob and can either be assigned in one place, or shared between multiple entities or blobs.
15 |
16 | For example:
17 | ```cs
18 | private class NodeDefinition : ScriptableObject, IConvertScriptableObjectToBlob
19 | {
20 | public int Value = 0;
21 |
22 | public NodeDefinition Left;
23 | public NodeDefinition Right;
24 |
25 | public BlobAssetReference Convert(ScriptableObjectConversionSystem conversion)
26 | {
27 | var builder = new BlobBuilder(Allocator.Temp);
28 |
29 | ref NodeBlob dst = ref builder.ConstructRoot();
30 |
31 | BlobAssetReference left = Left != null
32 | ? conversion.GetBlob(Left)
33 | : BlobAssetReference.Null;
34 |
35 | BlobAssetReference right = Right != null
36 | ? conversion.GetBlob(Right)
37 | : BlobAssetReference.Null;
38 |
39 | dst.Left = left;
40 | dst.Right = right;
41 | dst.Value = Value;
42 |
43 | BlobAssetReference result = builder.CreateBlobAssetReference(Allocator.Persistent);
44 |
45 | builder.Dispose();
46 |
47 | return result;
48 | }
49 | }
50 |
51 | private struct NodeBlob
52 | {
53 | public BlobAssetReference Left;
54 | public BlobAssetReference Right;
55 | public int Value;
56 | }
57 | ```
58 |
59 |
60 | ## **ScriptToBlobFunc<in T0, T1>**
61 | A delegate for manual conversions that do not utilize the IConvertScriptableObjectToBlob<T> workflow, primarily intended for scenarios where access to the original SO code is limited or non-existant. Such as Unity-developed config data, or 3rd-party assets/packages where code changes would be overwritten.
62 | It's recommended you use a static method as this will ensure a deterministic result if you intend to get the same SO conversion from multiple places.
63 |
64 | For example:
65 | ```cs
66 | private static BlobAssetReference ConvertCustomToBlob02(
67 | TestScriptableCustomFunc src,
68 | ScriptableObjectConversionSystem conversion)
69 | {
70 | var builder = new BlobBuilder(Allocator.Temp);
71 |
72 | ref TestBlob02 dst = ref builder.ConstructRoot();
73 |
74 | builder.AllocateString(ref dst.Foo, src.Foo);
75 |
76 | dst.Bar = src.Bar;
77 |
78 | ref BazData bazData = ref builder.Allocate(ref dst.Baz);
79 |
80 | bazData = src.Baz;
81 |
82 | BlobAssetReference result = builder.CreateBlobAssetReference(Allocator.Persistent);
83 |
84 | builder.Dispose();
85 |
86 | return result;
87 | }
88 |
89 | private class TestScriptableCustomFunc : ScriptableObject
90 | {
91 | public string Foo = kFooText;
92 | public uint Bar = kMeaningOfLifeUInt;
93 | public BazData Baz = sm_testBaz;
94 | }
95 |
96 | private struct TestBlob02
97 | {
98 | public BlobString Foo;
99 | public uint Bar;
100 | public BlobPtr Baz;
101 | }
102 |
103 | [Serializable]
104 | private struct BazData : IEquatable
105 | {
106 | public uint A;
107 | public uint B;
108 | public uint C;
109 | public uint D;
110 |
111 | public BazData(uint a, uint b, uint c, uint d)
112 | {
113 | A = a;
114 | B = b;
115 | C = c;
116 | D = d;
117 | }
118 |
119 | public bool Equals(BazData other) => A == other.A && B == other.B && C == other.C && D == other.D;
120 |
121 | public override bool Equals(object obj) => obj is BazData other && Equals(other);
122 |
123 | [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
124 | public override int GetHashCode()
125 | {
126 | unchecked
127 | {
128 | int hashCode = (int) A;
129 | hashCode = (hashCode * 397) ^ (int) B;
130 | hashCode = (hashCode * 397) ^ (int) C;
131 | hashCode = (hashCode * 397) ^ (int) D;
132 |
133 | return hashCode;
134 | }
135 | }
136 | }
137 | ```
--------------------------------------------------------------------------------
/Samples~/ConfigFileLoading/Contents/ConfigFileLoading.unity:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!29 &1
4 | OcclusionCullingSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 2
7 | m_OcclusionBakeSettings:
8 | smallestOccluder: 5
9 | smallestHole: 0.25
10 | backfaceThreshold: 100
11 | m_SceneGUID: 00000000000000000000000000000000
12 | m_OcclusionCullingData: {fileID: 0}
13 | --- !u!104 &2
14 | RenderSettings:
15 | m_ObjectHideFlags: 0
16 | serializedVersion: 9
17 | m_Fog: 0
18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
19 | m_FogMode: 3
20 | m_FogDensity: 0.01
21 | m_LinearFogStart: 0
22 | m_LinearFogEnd: 300
23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
26 | m_AmbientIntensity: 1
27 | m_AmbientMode: 0
28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
29 | m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
30 | m_HaloStrength: 0.5
31 | m_FlareStrength: 1
32 | m_FlareFadeSpeed: 3
33 | m_HaloTexture: {fileID: 0}
34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
35 | m_DefaultReflectionMode: 0
36 | m_DefaultReflectionResolution: 128
37 | m_ReflectionBounces: 1
38 | m_ReflectionIntensity: 1
39 | m_CustomReflection: {fileID: 0}
40 | m_Sun: {fileID: 0}
41 | m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
42 | m_UseRadianceAmbientProbe: 0
43 | --- !u!157 &3
44 | LightmapSettings:
45 | m_ObjectHideFlags: 0
46 | serializedVersion: 11
47 | m_GIWorkflowMode: 1
48 | m_GISettings:
49 | serializedVersion: 2
50 | m_BounceScale: 1
51 | m_IndirectOutputScale: 1
52 | m_AlbedoBoost: 1
53 | m_EnvironmentLightingMode: 0
54 | m_EnableBakedLightmaps: 1
55 | m_EnableRealtimeLightmaps: 1
56 | m_LightmapEditorSettings:
57 | serializedVersion: 12
58 | m_Resolution: 2
59 | m_BakeResolution: 40
60 | m_AtlasSize: 1024
61 | m_AO: 0
62 | m_AOMaxDistance: 1
63 | m_CompAOExponent: 1
64 | m_CompAOExponentDirect: 0
65 | m_ExtractAmbientOcclusion: 0
66 | m_Padding: 2
67 | m_LightmapParameters: {fileID: 0}
68 | m_LightmapsBakeMode: 1
69 | m_TextureCompression: 1
70 | m_FinalGather: 0
71 | m_FinalGatherFiltering: 1
72 | m_FinalGatherRayCount: 256
73 | m_ReflectionCompression: 2
74 | m_MixedBakeMode: 2
75 | m_BakeBackend: 1
76 | m_PVRSampling: 1
77 | m_PVRDirectSampleCount: 32
78 | m_PVRSampleCount: 512
79 | m_PVRBounces: 2
80 | m_PVREnvironmentSampleCount: 256
81 | m_PVREnvironmentReferencePointCount: 2048
82 | m_PVRFilteringMode: 1
83 | m_PVRDenoiserTypeDirect: 1
84 | m_PVRDenoiserTypeIndirect: 1
85 | m_PVRDenoiserTypeAO: 1
86 | m_PVRFilterTypeDirect: 0
87 | m_PVRFilterTypeIndirect: 0
88 | m_PVRFilterTypeAO: 0
89 | m_PVREnvironmentMIS: 1
90 | m_PVRCulling: 1
91 | m_PVRFilteringGaussRadiusDirect: 1
92 | m_PVRFilteringGaussRadiusIndirect: 5
93 | m_PVRFilteringGaussRadiusAO: 2
94 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5
95 | m_PVRFilteringAtrousPositionSigmaIndirect: 2
96 | m_PVRFilteringAtrousPositionSigmaAO: 1
97 | m_ExportTrainingData: 0
98 | m_TrainingDataDestination: TrainingData
99 | m_LightingDataAsset: {fileID: 0}
100 | m_UseShadowmask: 1
101 | --- !u!196 &4
102 | NavMeshSettings:
103 | serializedVersion: 2
104 | m_ObjectHideFlags: 0
105 | m_BuildSettings:
106 | serializedVersion: 2
107 | agentTypeID: 0
108 | agentRadius: 0.5
109 | agentHeight: 2
110 | agentSlope: 45
111 | agentClimb: 0.4
112 | ledgeDropHeight: 0
113 | maxJumpAcrossDistance: 0
114 | minRegionArea: 2
115 | manualCellSize: 0
116 | cellSize: 0.16666667
117 | manualTileSize: 0
118 | tileSize: 256
119 | accuratePlacement: 0
120 | debug:
121 | m_Flags: 0
122 | m_NavMeshData: {fileID: 0}
123 | --- !u!1 &401652032
124 | GameObject:
125 | m_ObjectHideFlags: 0
126 | m_CorrespondingSourceObject: {fileID: 0}
127 | m_PrefabInstance: {fileID: 0}
128 | m_PrefabAsset: {fileID: 0}
129 | serializedVersion: 6
130 | m_Component:
131 | - component: {fileID: 401652033}
132 | - component: {fileID: 401652034}
133 | m_Layer: 0
134 | m_Name: FileLoadingDriver
135 | m_TagString: Untagged
136 | m_Icon: {fileID: 0}
137 | m_NavMeshLayer: 0
138 | m_StaticEditorFlags: 0
139 | m_IsActive: 1
140 | --- !u!4 &401652033
141 | Transform:
142 | m_ObjectHideFlags: 0
143 | m_CorrespondingSourceObject: {fileID: 0}
144 | m_PrefabInstance: {fileID: 0}
145 | m_PrefabAsset: {fileID: 0}
146 | m_GameObject: {fileID: 401652032}
147 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
148 | m_LocalPosition: {x: 0, y: 0, z: 0}
149 | m_LocalScale: {x: 1, y: 1, z: 1}
150 | m_Children: []
151 | m_Father: {fileID: 0}
152 | m_RootOrder: 0
153 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
154 | --- !u!114 &401652034
155 | MonoBehaviour:
156 | m_ObjectHideFlags: 0
157 | m_CorrespondingSourceObject: {fileID: 0}
158 | m_PrefabInstance: {fileID: 0}
159 | m_PrefabAsset: {fileID: 0}
160 | m_GameObject: {fileID: 401652032}
161 | m_Enabled: 1
162 | m_EditorHideFlags: 0
163 | m_Script: {fileID: 11500000, guid: 5feedfb56761f0c4ab76171409ced649, type: 3}
164 | m_Name:
165 | m_EditorClassIdentifier:
166 | m_fileName: TestConfig.ini
167 | m_iniContents: 'foo=42
168 |
169 | bar="hello world"
170 |
171 | baz=12.34
172 |
173 | qux=Slow
174 |
175 | '
176 |
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid/ScriptableObjectConversionSystem.cs:
--------------------------------------------------------------------------------
1 | #define DETAIL_MARKERS
2 | using System;
3 | using Unity.Collections;
4 | using Unity.Collections.LowLevel.Unsafe;
5 | using Unity.Entities;
6 | using Unity.Profiling;
7 | using UnityEngine;
8 | using UnityEngine.Assertions;
9 |
10 | namespace Hydrogen.Entities
11 | {
12 | ///
13 | /// Defines a delegate that takes a and returns a
14 | ///
15 | /// The SO to convert.
16 | /// The , allowing us to get references to other converted blobs.
17 | /// The concrete type of the SO to be converted.
18 | /// The struct type our Blob asset reference points to.
19 | public delegate BlobAssetReference ScriptToBlobFunc(
20 | T0 src,
21 | ScriptableObjectConversionSystem convert)
22 | where T0 : ScriptableObject
23 | where T1 : struct;
24 |
25 | // TODO: Handle Acyclic graphs? Someone will try to do that eventually. Probably me...
26 |
27 | ///
28 | /// A System similar to the , but helps with converting to .
29 | ///
30 | [DisableAutoCreation]
31 | public class ScriptableObjectConversionSystem : ComponentSystem
32 | {
33 | private unsafe struct BlobData
34 | {
35 | [NativeDisableUnsafePtrRestriction] private byte* m_blobRef;
36 |
37 | public static BlobData Create(BlobAssetReference reference, int identifier)
38 | where T0 : struct
39 | {
40 | BlobData blobData = default;
41 |
42 | UnsafeUtility.CopyStructureToPtr(ref reference, &blobData.m_blobRef);
43 |
44 | return blobData;
45 | }
46 |
47 | public BlobAssetReference AsReference()
48 | where T0 : struct
49 | {
50 | Assert.IsTrue(m_blobRef != null);
51 |
52 | fixed (void* data = &m_blobRef)
53 | {
54 | UnsafeUtility.CopyPtrToStructure(data, out BlobAssetReference reference);
55 |
56 | return reference;
57 | }
58 | }
59 | }
60 |
61 | private NativeHashMap m_scriptableToBlob;
62 |
63 | private GameObjectConversionSystem m_goConversionSystem;
64 |
65 | public GameObjectConversionSystem GoConversionSystem
66 | {
67 | get
68 | {
69 | if (m_goConversionSystem != null)
70 | return m_goConversionSystem;
71 |
72 | m_goConversionSystem = World.GetExistingSystem();
73 | Assert.IsNotNull(
74 | m_goConversionSystem,
75 | "Null GO Conversion system, did you mean to Get this System from the GameObject conversion world instead of the Destination World?");
76 |
77 | return m_goConversionSystem;
78 | }
79 | }
80 |
81 | protected override void OnCreate() =>
82 | m_scriptableToBlob = new NativeHashMap(100 * 1000, Allocator.Persistent);
83 |
84 | protected override void OnDestroy()
85 | {
86 | m_scriptableToBlob.Dispose();
87 | }
88 |
89 | #if DETAIL_MARKERS
90 | private ProfilerMarker m_createBlob = new ProfilerMarker("ScriptableObjectConversion.CreateBlob");
91 |
92 | private ProfilerMarker m_createBlobWithFunc =
93 | new ProfilerMarker("ScriptableObjectConversion.CreateBlobWithFunc");
94 | #endif
95 |
96 | private BlobData ConvertBlob(IConvertScriptableObjectToBlob src, int identifier)
97 | where T0 : struct
98 | {
99 | #if DETAIL_MARKERS
100 | using (m_createBlob.Auto())
101 | #endif
102 | {
103 | BlobAssetReference assetReference = src.Convert(this);
104 |
105 | return BlobData.Create(assetReference, identifier);
106 | }
107 | }
108 |
109 | private BlobData ConvertBlob(T0 obj, ScriptToBlobFunc func, int identifier)
110 | where T0 : ScriptableObject
111 | where T1 : struct
112 | {
113 | #if DETAIL_MARKERS
114 | using (m_createBlobWithFunc.Auto())
115 | #endif
116 | {
117 | BlobAssetReference assetReference = func.Invoke(obj, this);
118 |
119 | return BlobData.Create(assetReference, identifier);
120 | }
121 | }
122 |
123 | protected override void OnUpdate() { }
124 |
125 | ///
126 | /// Converts a of to a blob reference of .
127 | /// The SO must implement
128 | ///
129 | /// The ScriptableObject to convert
130 | /// Concrete type of ScriptableObject
131 | /// Type of the struct our Blob asset will reference
132 | /// The constructed
133 | public BlobAssetReference GetBlob(T0 obj)
134 | where T0 : ScriptableObject, IConvertScriptableObjectToBlob
135 | where T1 : struct
136 | {
137 | int identifier = new Vector3Int(obj.GetInstanceID(), typeof(T0).GetHashCode(), typeof(T1).GetHashCode())
138 | .GetHashCode();
139 |
140 | if (PreCheck(obj, identifier, out BlobAssetReference blob))
141 | return blob;
142 |
143 | BlobData data = ConvertBlob(obj, identifier);
144 |
145 | return PostCheck(identifier, data);
146 | }
147 |
148 | ///
149 | /// Converts a of to a blob reference of .
150 | /// The user must provide a manual conversion function.
151 | /// The SO need not implement , making it useful
152 | /// for converting pre-existing SO that are out of your control.
153 | ///
154 | /// The SO to convert.
155 | /// A delegate of . The concrete types must match.
156 | /// The concrete type of the SO.
157 | /// Type of the struct our Blob asset will reference.
158 | /// The constructed
159 | public BlobAssetReference GetBlob(T0 obj, ScriptToBlobFunc func)
160 | where T0 : ScriptableObject
161 | where T1 : struct
162 | {
163 | int identifier = new Vector2Int(obj.GetInstanceID(), func.GetHashCode()).GetHashCode();
164 |
165 | if (PreCheck(obj, identifier, out BlobAssetReference blob))
166 | return blob;
167 |
168 | BlobData data = ConvertBlob(obj, func, identifier);
169 |
170 | return PostCheck(identifier, data);
171 | }
172 |
173 | private BlobAssetReference PostCheck(int instanceId, BlobData data)
174 | where T0 : struct
175 | {
176 | if (m_scriptableToBlob.TryAdd(instanceId, data))
177 | return data.AsReference();
178 |
179 | data.AsReference().Dispose();
180 |
181 | throw new InvalidOperationException();
182 | }
183 |
184 | private bool PreCheck(T0 obj, int identifier, out BlobAssetReference blobAssetReference)
185 | where T0 : ScriptableObject
186 | where T1 : struct
187 | {
188 | blobAssetReference = default;
189 |
190 | if (obj == null)
191 | throw new NullReferenceException();
192 |
193 | if (!m_scriptableToBlob.TryGetValue(identifier, out BlobData data))
194 | return false;
195 |
196 | blobAssetReference = data.AsReference();
197 |
198 | return true;
199 | }
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/Hydrogen.Entities.Hybrid.Tests/ConvertScriptableObjectToBlobTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using NUnit.Framework;
4 | using Unity.Collections;
5 | using Unity.Entities;
6 | using Unity.Entities.Tests;
7 | using UnityEngine;
8 | using UnityEngine.TestTools.Utils;
9 | using Object = UnityEngine.Object;
10 |
11 | // ReSharper disable once CheckNamespace
12 | namespace Hydrogen.Entities.Tests
13 | {
14 | public class ConvertScriptableObjectToBlobTests : ECSTestsFixture
15 | {
16 | const float kMeaningOfLifeFloat = 42.0f;
17 | const uint kMeaningOfLifeUInt = 42u;
18 | const string kFooText = "Lorem Ipsum";
19 | static readonly int[] sm_integers = {0, 1, 2, 3};
20 | static readonly BazData sm_testBaz = new BazData(1,2,3,4);
21 |
22 | GameObjectConversionSettings MakeDefaultSettings() =>
23 | new GameObjectConversionSettings()
24 | {
25 | DestinationWorld = World,
26 | ConversionFlags = GameObjectConversionUtility.ConversionFlags.AssignName
27 | };
28 |
29 | struct TestBlob01
30 | {
31 | public BlobString Name;
32 | public BlobPtr AFloat;
33 | public BlobArray Ints;
34 | }
35 |
36 | class TestScriptableInterface01 : ScriptableObject, IConvertScriptableObjectToBlob
37 | {
38 | public float AFloat = kMeaningOfLifeFloat;
39 | public int[] Integers = sm_integers;
40 |
41 | public BlobAssetReference Convert(ScriptableObjectConversionSystem conversion)
42 | {
43 | var builder = new BlobBuilder(Allocator.Temp);
44 |
45 | ref var target = ref builder.ConstructRoot();
46 |
47 | if(!string.IsNullOrEmpty(name) && name.Length > 0)
48 | builder.AllocateString(ref target.Name, name);
49 | else
50 | target.Name = new BlobString();
51 |
52 | ref var afloat = ref builder.Allocate(ref target.AFloat);
53 |
54 | afloat = AFloat;
55 |
56 | var intsLen = Integers.Length;
57 |
58 | if (intsLen > 0)
59 | builder.Construct(ref target.Ints, Integers);
60 | else
61 | target.Ints = new BlobArray();
62 |
63 | var assetRef =
64 | builder.CreateBlobAssetReference(Allocator.Persistent);
65 |
66 | builder.Dispose();
67 |
68 | return assetRef;
69 | }
70 | }
71 |
72 | class TestScriptableCustomFunc : ScriptableObject
73 | {
74 | public string Foo = kFooText;
75 | public uint Bar = kMeaningOfLifeUInt;
76 | public BazData Baz = sm_testBaz;
77 | }
78 |
79 | struct TestBlob02
80 | {
81 | public BlobString Foo;
82 | public uint Bar;
83 | public BlobPtr Baz;
84 | }
85 |
86 | class NodeDefinition : ScriptableObject, IConvertScriptableObjectToBlob
87 | {
88 | public int Value;
89 |
90 | public NodeDefinition Left;
91 | public NodeDefinition Right;
92 |
93 | public BlobAssetReference Convert(ScriptableObjectConversionSystem conversion)
94 | {
95 | var builder = new BlobBuilder(Allocator.Temp);
96 |
97 | ref var dst = ref builder.ConstructRoot();
98 |
99 | var left = Left != null
100 | ? conversion.GetBlob(Left)
101 | : BlobAssetReference.Null;
102 |
103 | var right = Right != null
104 | ? conversion.GetBlob(Right)
105 | : BlobAssetReference.Null;
106 |
107 | dst.Left = left;
108 | dst.Right = right;
109 | dst.Value = Value;
110 |
111 | var result = builder.CreateBlobAssetReference(Allocator.Persistent);
112 |
113 | builder.Dispose();
114 |
115 | return result;
116 | }
117 | }
118 |
119 | struct NodeBlob
120 | {
121 | public BlobAssetReference Left;
122 | public BlobAssetReference Right;
123 | public int Value;
124 | }
125 |
126 | [Serializable]
127 | struct BazData : IEquatable
128 | {
129 | public uint A;
130 | public uint B;
131 | public uint C;
132 | public uint D;
133 |
134 | public BazData(uint a, uint b, uint c, uint d)
135 | {
136 | A = a;
137 | B = b;
138 | C = c;
139 | D = d;
140 | }
141 |
142 | public bool Equals(BazData other) => A == other.A && B == other.B && C == other.C && D == other.D;
143 |
144 | public override bool Equals(object obj) => obj is BazData other && Equals(other);
145 |
146 | [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
147 | public override int GetHashCode()
148 | {
149 | unchecked
150 | {
151 | var hashCode = (int) A;
152 | hashCode = (hashCode * 397) ^ (int) B;
153 | hashCode = (hashCode * 397) ^ (int) C;
154 | hashCode = (hashCode * 397) ^ (int) D;
155 |
156 | return hashCode;
157 | }
158 | }
159 | }
160 |
161 | static BlobAssetReference ConvertCustomToBlob02(
162 | TestScriptableCustomFunc src,
163 | ScriptableObjectConversionSystem conversion)
164 | {
165 | var builder = new BlobBuilder(Allocator.Temp);
166 |
167 | ref var dst = ref builder.ConstructRoot();
168 |
169 | builder.AllocateString(ref dst.Foo, src.Foo);
170 |
171 | dst.Bar = src.Bar;
172 |
173 | ref var bazData = ref builder.Allocate(ref dst.Baz);
174 |
175 | bazData = src.Baz;
176 |
177 | var result = builder.CreateBlobAssetReference(Allocator.Persistent);
178 |
179 | builder.Dispose();
180 |
181 | return result;
182 | }
183 |
184 | static BlobAssetReference ConvertInterfaceToBlob01(
185 | TestScriptableInterface01 src,
186 | ScriptableObjectConversionSystem conversion)
187 | {
188 | var builder = new BlobBuilder(Allocator.Temp);
189 |
190 | ref var target = ref builder.ConstructRoot();
191 |
192 | if(!string.IsNullOrEmpty(src.name))
193 | builder.AllocateString(ref target.Name, src.name);
194 | else
195 | target.Name = new BlobString();
196 |
197 | ref var afloat = ref builder.Allocate(ref target.AFloat);
198 |
199 | afloat = src.AFloat;
200 |
201 | var intsLen = src.Integers.Length;
202 |
203 | if (intsLen > 0)
204 | builder.Construct(ref target.Ints, src.Integers);
205 | else
206 | target.Ints = new BlobArray();
207 |
208 | var result =
209 | builder.CreateBlobAssetReference(Allocator.Persistent);
210 |
211 | builder.Dispose();
212 |
213 | return result;
214 | }
215 |
216 | static readonly ScriptToBlobFunc sm_convertCustomToBlob02 =
217 | ConvertCustomToBlob02;
218 |
219 | static readonly ScriptToBlobFunc sm_convertInterfaceToBlob01 =
220 | ConvertInterfaceToBlob01;
221 |
222 | static void TryDisposeBlob(in BlobAssetReference reference)
223 | where T : struct
224 | {
225 | // ReSharper disable once PossiblyImpureMethodCallOnReadonlyVariable
226 | if(reference.IsCreated) reference.Dispose();
227 | }
228 |
229 | ScriptableObjectConversionSystem m_conversion;
230 |
231 | [SetUp]
232 | public override void Setup()
233 | {
234 | base.Setup();
235 | m_conversion = World.GetOrCreateSystem();
236 | }
237 |
238 | [Test]
239 | public void ConvertScriptableObjectToBlob_WithInterface()
240 | {
241 | var src = ScriptableObject.CreateInstance();
242 | var reference = BlobAssetReference.Null;
243 |
244 | try
245 | {
246 | Assert.NotNull(src);
247 |
248 | // ReSharper disable once CompareOfFloatsByEqualityOperator
249 | Assert.IsTrue(src.AFloat == kMeaningOfLifeFloat);
250 | Assert.IsTrue(src.Integers.Length == 4);
251 | Assert.IsTrue(src.Integers[0] == 0 && src.Integers[1] == 1 && src.Integers[2] == 2 && src.Integers[3] == 3);
252 |
253 | reference = m_conversion.GetBlob(src);
254 | Assert.IsTrue(reference.IsCreated);
255 |
256 | ref var dst = ref reference.Value;
257 | // ReSharper disable once CompareOfFloatsByEqualityOperator
258 | Assert.IsTrue(src.AFloat == dst.AFloat.Value);
259 | Assert.IsTrue(src.name == dst.Name.ToString());
260 | Assert.IsTrue(src.Integers.Length == dst.Ints.Length);
261 |
262 | for (var i = 0; i < 4; i++)
263 | Assert.IsTrue(src.Integers[i] == dst.Ints[i]);
264 | }
265 | finally
266 | {
267 | Object.DestroyImmediate(src);
268 | TryDisposeBlob(reference);
269 | }
270 | }
271 |
272 | [Test]
273 | public void ConvertScriptableObjectToBlob_WithFunction()
274 | {
275 | var src = ScriptableObject.CreateInstance();
276 | var reference = BlobAssetReference.Null;
277 |
278 | try
279 | {
280 | Assert.NotNull(src);
281 |
282 | // ReSharper disable once CompareOfFloatsByEqualityOperator
283 | Assert.IsTrue(src.Foo == kFooText);
284 | Assert.IsTrue(src.Bar == kMeaningOfLifeUInt);
285 | Assert.IsTrue(src.Baz.Equals(sm_testBaz));
286 |
287 | reference = m_conversion.GetBlob(src, sm_convertCustomToBlob02);
288 | Assert.IsTrue(reference.IsCreated);
289 |
290 | ref var dst = ref reference.Value;
291 | // ReSharper disable once CompareOfFloatsByEqualityOperator
292 | Assert.IsTrue(src.Foo == dst.Foo.ToString());
293 | Assert.IsTrue(src.Bar == dst.Bar);
294 | Assert.IsTrue(src.Baz.Equals(dst.Baz.Value));
295 | }
296 | finally
297 | {
298 | Object.DestroyImmediate(src);
299 | TryDisposeBlob(reference);
300 | }
301 | }
302 |
303 | [Test]
304 | public void ConvertScriptableObjectToBlob_WithBoth()
305 | {
306 | var original = ScriptableObject.CreateInstance();
307 | original.name = "Test";
308 |
309 | var interfaceTest = BlobAssetReference.Null;
310 | var functionTest = BlobAssetReference.Null;
311 |
312 | try
313 | {
314 | Assert.IsTrue(original.name == "Test");
315 | Assert.IsTrue(Utils.AreFloatsEqual(original.AFloat, kMeaningOfLifeFloat, float.Epsilon));
316 | Assert.IsTrue(
317 | original.Integers[0] == sm_integers[0]
318 | && original.Integers[1] == sm_integers[1]
319 | && original.Integers[2] == sm_integers[2]
320 | && original.Integers[3] == sm_integers[3]);
321 |
322 | interfaceTest = m_conversion.GetBlob(original);
323 | functionTest = m_conversion.GetBlob(original, sm_convertInterfaceToBlob01);
324 |
325 | ref var interfaceBlob = ref interfaceTest.Value;
326 | ref var functionBlob = ref functionTest.Value;
327 |
328 | Assert.IsTrue(interfaceBlob.Name.ToString() == functionBlob.Name.ToString());
329 | Assert.IsTrue(
330 | Utils.AreFloatsEqual(interfaceBlob.AFloat.Value, functionBlob.AFloat.Value, float.Epsilon));
331 | Assert.IsTrue(interfaceBlob.Ints.Length == functionBlob.Ints.Length);
332 |
333 | for (var i = 0; i < 4; i++)
334 | Assert.IsTrue(interfaceBlob.Ints[i] == functionBlob.Ints[i]);
335 |
336 | Assert.IsTrue(interfaceBlob.Name.ToString() == original.name);
337 | Assert.IsTrue(Utils.AreFloatsEqual(interfaceBlob.AFloat.Value, original.AFloat, float.Epsilon));
338 | Assert.IsTrue(interfaceBlob.Ints.Length == original.Integers.Length);
339 |
340 | for (var i = 0; i < 4; i++)
341 | Assert.IsTrue(interfaceBlob.Ints[i] == original.Integers[i]);
342 | }
343 | finally
344 | {
345 | Object.DestroyImmediate(original);
346 | TryDisposeBlob(interfaceTest);
347 | TryDisposeBlob(functionTest);
348 | }
349 | }
350 |
351 | [Test]
352 | public void ConvertScriptableObjectToBlob_WithChainedBlobReferences()
353 | {
354 | var a = ScriptableObject.CreateInstance();
355 | var b = ScriptableObject.CreateInstance();
356 | var c = ScriptableObject.CreateInstance();
357 | var d = ScriptableObject.CreateInstance();
358 | var e = ScriptableObject.CreateInstance();
359 |
360 | a.Value = 0;
361 | a.Left = b;
362 | a.Right = c;
363 |
364 | b.Value = 1;
365 | b.Left = d;
366 | b.Right = null;
367 |
368 | c.Value = 2;
369 | c.Left = null;
370 | c.Right = e;
371 |
372 | d.Value = 3;
373 | e.Value = 4;
374 |
375 | var aBlob = BlobAssetReference.Null;
376 | var bBlob = BlobAssetReference.Null;
377 | var cBlob = BlobAssetReference.Null;
378 | var dBlob = BlobAssetReference.Null;
379 | var eBlob = BlobAssetReference.Null;
380 |
381 | try
382 | {
383 | aBlob = m_conversion.GetBlob(a);
384 | bBlob = m_conversion.GetBlob(b);
385 | cBlob = m_conversion.GetBlob(c);
386 | dBlob = m_conversion.GetBlob(d);
387 | eBlob = m_conversion.GetBlob(e);
388 |
389 | Assert.IsTrue(aBlob.Value.Value == 0);
390 | Assert.IsTrue(bBlob.Value.Value == 1);
391 | Assert.IsTrue(cBlob.Value.Value == 2);
392 | Assert.IsTrue(dBlob.Value.Value == 3);
393 | Assert.IsTrue(eBlob.Value.Value == 4);
394 |
395 | Assert.IsTrue(aBlob.Value.Left == bBlob);
396 | Assert.IsTrue(aBlob.Value.Right == cBlob);
397 | Assert.IsTrue(bBlob.Value.Left == dBlob);
398 | Assert.IsTrue(bBlob.Value.Right == BlobAssetReference.Null);
399 | Assert.IsTrue(cBlob.Value.Left == BlobAssetReference.Null);
400 | Assert.IsTrue(cBlob.Value.Right == eBlob);
401 | Assert.IsTrue(dBlob.Value.Left == BlobAssetReference.Null);
402 | Assert.IsTrue(dBlob.Value.Right == BlobAssetReference.Null);
403 | Assert.IsTrue(eBlob.Value.Left == BlobAssetReference.Null);
404 | Assert.IsTrue(eBlob.Value.Right == BlobAssetReference.Null);
405 | }
406 | finally
407 | {
408 | Object.DestroyImmediate(a);
409 | Object.DestroyImmediate(b);
410 | Object.DestroyImmediate(c);
411 | Object.DestroyImmediate(d);
412 | Object.DestroyImmediate(e);
413 |
414 | TryDisposeBlob(aBlob);
415 | TryDisposeBlob(bBlob);
416 | TryDisposeBlob(cBlob);
417 | TryDisposeBlob(dBlob);
418 | TryDisposeBlob(eBlob);
419 | }
420 | }
421 | }
422 | }
423 |
--------------------------------------------------------------------------------