├── Changelog.md ├── Changelog.md.meta ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── IPoolable.cs ├── IPoolable.cs.meta ├── Pool.cs ├── Pool.cs.meta ├── PoolHelper.cs ├── PoolHelper.cs.meta ├── PoolInstaller.cs ├── PoolInstaller.cs.meta ├── Poolable.cs ├── Poolable.cs.meta ├── com.intothedev.objectpooling.asmdef └── com.intothedev.objectpooling.asmdef.meta ├── package.json └── package.json.meta /Changelog.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Changelog.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 70a6d47e8c04e9c48b3c84993d929510 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Vladislav Kinyashov 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 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 67827da8471d03a4b876983b23213782 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Object Pooling for Unity 2 | 3 | ### TODO 4 | - [x] Rename API to make it more clarifying 5 | 6 | ## Features 7 | - Faster in terms of performance than Instantiate/Destroy (Test at the end of README) 8 | - Easy to use 9 | - Easy to integrate with already written spawn systems 10 | - Callbacks OnReuse & OnRelease to reset object's state 11 | 12 | ## How to Install 13 | ### Git Installation (Best way to get latest version) 14 | 15 | If you have Git on your computer, you can open Package Manager indside Unity, select "Add package from Git url...", and paste link ```https://github.com/IntoTheDev/Object-Pooling-for-Unity.git``` 16 | 17 | or 18 | 19 | Open the manifest.json file of your Unity project. 20 | Add ```"com.intothedev.objectpooling": "https://github.com/IntoTheDev/Object-Pooling-for-Unity.git"``` 21 | 22 | ### Manual Installation (Version can be outdated) 23 | Download latest package from the Release section. 24 | Import ObjectPooling.unitypackage to your Unity Project 25 | 26 | ## Usage 27 | ### How to populate pool: 28 | ```csharp 29 | using ToolBox.Pools; 30 | using UnityEngine; 31 | 32 | public class Spawner : MonoBehaviour 33 | { 34 | [SerializeField] private GameObject _prefab = null; 35 | 36 | private void Awake() 37 | { 38 | _prefab.Populate(count: 50); 39 | } 40 | } 41 | ``` 42 | 43 | Also, you can just put PoolInstaller component on any object on Scene and select which objects you want to prepopulate 44 | 45 | ![](https://i.imgur.com/gnyZ0RQ.png) 46 | 47 | ### How to clear pool and it's instances 48 | ```csharp 49 | using ToolBox.Pools; 50 | using UnityEngine; 51 | 52 | public class Spawner : MonoBehaviour 53 | { 54 | [SerializeField] private GameObject _prefab = null; 55 | 56 | private void Awake() 57 | { 58 | _prefab.Populate(count: 50); 59 | 60 | // If destroy active is true then even active instances will be destroyed 61 | _prefab.Clear(destroyActive: true) 62 | } 63 | } 64 | ``` 65 | 66 | ### How to get object from pool: 67 | ```csharp 68 | using ToolBox.Pools; 69 | using UnityEngine; 70 | 71 | public class Spawner : MonoBehaviour 72 | { 73 | [SerializeField] private GameObject _prefab = null; 74 | 75 | public void Spawn() 76 | { 77 | _prefab.Reuse(transform.position, transform.rotation); 78 | 79 | // Get object from pool with component 80 | _prefab.Reuse(transform.position, transform.rotation).isKinematic = true; 81 | } 82 | } 83 | ``` 84 | 85 | ### How to release object: 86 | ```csharp 87 | using ToolBox.Pools; 88 | using UnityEngine; 89 | 90 | public class Spawner : MonoBehaviour 91 | { 92 | [SerializeField] private GameObject _prefab = null; 93 | 94 | public void Spawn() 95 | { 96 | var instance = _prefab.Reuse(transform.position, transform.rotation); 97 | instance.Release(); 98 | } 99 | } 100 | ``` 101 | 102 | ### How to use callbacks: 103 | ```csharp 104 | using ToolBox.Pools; 105 | using UnityEngine; 106 | 107 | public class Health : MonoBehaviour, IPoolable 108 | { 109 | [SerializeField] private float _maxHealth = 100f; 110 | 111 | private float _health = 0f; 112 | 113 | // Awake will be called on first _prefab.Reuse() 114 | private void Awake() 115 | { 116 | OnReuse(); 117 | } 118 | 119 | // IPoolable method 120 | /// 121 | /// This method will be called on 2nd Reuse call. 122 | /// Use Unity's Awake method for first initialization and this method for others 123 | /// 124 | public void OnReuse() 125 | { 126 | _health = _maxHealth; 127 | } 128 | 129 | // IPoolable method 130 | public void OnRelease() { } 131 | } 132 | ``` 133 | 134 | ### Peformance test: 135 | Creating and destroying 1000 objects. 136 | 137 | #### Instantiate/Destroy: 138 | 139 | ```csharp 140 | using Sirenix.OdinInspector; 141 | using System.Diagnostics; 142 | using UnityEngine; 143 | 144 | public class Tester : MonoBehaviour 145 | { 146 | [SerializeField] private GameObject _object = null; 147 | 148 | [Button] 149 | private void Test() 150 | { 151 | Stopwatch stopwatch = new Stopwatch(); 152 | stopwatch.Start(); 153 | 154 | for (int i = 0; i < 1000; i++) 155 | { 156 | var instance = Instantiate(_object); 157 | Destroy(instance); 158 | } 159 | 160 | stopwatch.Stop(); 161 | print($"Milliseconds: {stopwatch.ElapsedMilliseconds}"); 162 | } 163 | } 164 | ``` 165 | ##### Result: [16:26:15] Milliseconds: 6 166 | 167 | #### Get/Release: 168 | 169 | ```csharp 170 | using Sirenix.OdinInspector; 171 | using System.Diagnostics; 172 | using ToolBox.Pools; 173 | using UnityEngine; 174 | 175 | public class Tester : MonoBehaviour 176 | { 177 | [SerializeField] private GameObject _object = null; 178 | 179 | private void Awake() 180 | { 181 | _object.Populate(1000); 182 | } 183 | 184 | [Button] 185 | private void Test() 186 | { 187 | Stopwatch stopwatch = new Stopwatch(); 188 | stopwatch.Start(); 189 | 190 | for (int i = 0; i < 1000; i++) 191 | { 192 | var instance = _object.Reuse(); 193 | instance.Release(); 194 | } 195 | 196 | stopwatch.Stop(); 197 | print($"Milliseconds: {stopwatch.ElapsedMilliseconds}"); 198 | } 199 | } 200 | ``` 201 | ##### Result: [16:29:36] Milliseconds: 2 202 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d41be38fe4e2f5640a1e8bfc5a02f2e7 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8a64ab054496339458894dbf4d86efed 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/IPoolable.cs: -------------------------------------------------------------------------------- 1 | namespace ToolBox.Pools 2 | { 3 | public interface IPoolable 4 | { 5 | /// 6 | /// This method will be called on 2nd Reuse call. 7 | /// Use Unity's Awake method for first initialization and this method for others 8 | /// 9 | void OnReuse(); 10 | void OnRelease(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Runtime/IPoolable.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9472cf5f3f62b224a92a58aed93a7a3f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Pool.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace ToolBox.Pools 5 | { 6 | internal sealed class Pool 7 | { 8 | private GameObject _source; 9 | private Poolable _prototype; 10 | private Stack _instances; 11 | private List _allInstances; 12 | private readonly Quaternion _rotation; 13 | private readonly Vector3 _scale; 14 | private readonly bool _prototypeIsNotSource; 15 | 16 | private static readonly Dictionary _prefabLookup = new Dictionary(64); 17 | private static readonly Dictionary _instanceLookup = new Dictionary(512); 18 | 19 | private const int InitialSize = 128; 20 | 21 | public Pool(GameObject prefab) 22 | { 23 | _source = prefab; 24 | _prototype = prefab.GetComponent(); 25 | 26 | if (_prototype == null) 27 | { 28 | _prototype = Object.Instantiate(prefab).AddComponent(); 29 | Object.DontDestroyOnLoad(_prototype); 30 | _prototype.gameObject.SetActive(false); 31 | _prototypeIsNotSource = true; 32 | } 33 | 34 | _instances = new Stack(InitialSize); 35 | _allInstances = new List(InitialSize); 36 | _prefabLookup.Add(_source, this); 37 | 38 | var transform = prefab.transform; 39 | _rotation = transform.rotation; 40 | _scale = transform.localScale; 41 | } 42 | 43 | public static Pool GetPoolByPrefab(GameObject prefab, bool create = true) 44 | { 45 | var hasPool = _prefabLookup.TryGetValue(prefab, out var pool); 46 | 47 | if (!hasPool && create) 48 | pool = new Pool(prefab); 49 | 50 | return pool; 51 | } 52 | 53 | public static bool GetPoolByInstance(GameObject instance, out Pool pool) 54 | { 55 | return _instanceLookup.TryGetValue(instance, out pool); 56 | } 57 | 58 | public static void Remove(GameObject instance) 59 | { 60 | _instanceLookup.Remove(instance); 61 | } 62 | 63 | public void Populate(int count) 64 | { 65 | for (var i = 0; i < count; i++) 66 | { 67 | var instance = CreateInstance(); 68 | instance.gameObject.SetActive(false); 69 | _instances.Push(instance); 70 | } 71 | } 72 | 73 | public void Clear(bool destroyActive) 74 | { 75 | _prefabLookup.Remove(_source); 76 | 77 | foreach (var instance in _allInstances) 78 | { 79 | if (instance == null) 80 | continue; 81 | 82 | _instanceLookup.Remove(instance); 83 | 84 | if (!destroyActive && instance.activeInHierarchy) 85 | continue; 86 | 87 | Object.Destroy(instance); 88 | } 89 | 90 | if (_prototypeIsNotSource) 91 | Object.Destroy(_prototype.gameObject); 92 | 93 | _source = null; 94 | _prototype = null; 95 | _instances = null; 96 | _allInstances = null; 97 | } 98 | 99 | public GameObject Reuse() 100 | { 101 | var instance = GetInstance(); 102 | 103 | return instance.gameObject; 104 | } 105 | 106 | public GameObject Reuse(Transform parent) 107 | { 108 | var instance = GetInstance(); 109 | 110 | instance.transform.SetParent(parent); 111 | 112 | return instance.gameObject; 113 | } 114 | 115 | public GameObject Reuse(Transform parent, bool worldPositionStays) 116 | { 117 | var instance = GetInstance(); 118 | 119 | instance.transform.SetParent(parent, worldPositionStays); 120 | 121 | return instance.gameObject; 122 | } 123 | 124 | public GameObject Reuse(Vector3 position, Quaternion rotation) 125 | { 126 | var instance = GetInstance(); 127 | 128 | instance.transform.SetPositionAndRotation(position, rotation); 129 | 130 | return instance.gameObject; 131 | } 132 | 133 | public GameObject Reuse(Vector3 position, Quaternion rotation, Transform parent) 134 | { 135 | var instance = GetInstance(); 136 | var instanceTransform = instance.transform; 137 | 138 | instanceTransform.SetPositionAndRotation(position, rotation); 139 | instanceTransform.SetParent(parent); 140 | 141 | return instance.gameObject; 142 | } 143 | 144 | public void Release(GameObject instance) 145 | { 146 | var poolable = instance.GetComponent(); 147 | poolable.OnRelease(); 148 | 149 | instance.SetActive(false); 150 | 151 | var instanceTransform = instance.transform; 152 | instanceTransform.SetParent(null); 153 | instanceTransform.rotation = _rotation; 154 | instanceTransform.localScale = _scale; 155 | 156 | _instances.Push(poolable); 157 | } 158 | 159 | private Poolable GetInstance() 160 | { 161 | var count = _instances.Count; 162 | 163 | if (count != 0) 164 | { 165 | var instance = _instances.Pop(); 166 | 167 | if (instance == null) 168 | { 169 | count--; 170 | 171 | while (count != 0) 172 | { 173 | instance = _instances.Pop(); 174 | 175 | if (instance != null) 176 | { 177 | instance.OnReuse(); 178 | instance.gameObject.SetActive(true); 179 | 180 | return instance; 181 | } 182 | 183 | count--; 184 | } 185 | 186 | instance = CreateInstance(); 187 | instance.gameObject.SetActive(true); 188 | 189 | return instance; 190 | } 191 | 192 | instance.OnReuse(); 193 | instance.gameObject.SetActive(true); 194 | 195 | return instance; 196 | } 197 | else 198 | { 199 | var instance = CreateInstance(); 200 | instance.gameObject.SetActive(true); 201 | 202 | return instance; 203 | } 204 | } 205 | 206 | private Poolable CreateInstance() 207 | { 208 | var instance = Object.Instantiate(_prototype); 209 | var instanceGameObject = instance.gameObject; 210 | 211 | _instanceLookup.Add(instanceGameObject, this); 212 | _allInstances.Add(instanceGameObject); 213 | 214 | return instance; 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /Runtime/Pool.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 82a21262723a41b4a8603b763868a09c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/PoolHelper.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace ToolBox.Pools 4 | { 5 | public static class PoolHelper 6 | { 7 | public static void Populate(this GameObject prefab, int count) 8 | { 9 | Pool.GetPoolByPrefab(prefab).Populate(count); 10 | } 11 | 12 | public static void Clear(this GameObject prefab, bool destroyActive) 13 | { 14 | Pool.GetPoolByPrefab(prefab, false)?.Clear(destroyActive); 15 | } 16 | 17 | public static GameObject Reuse(this GameObject prefab) 18 | { 19 | return Pool.GetPoolByPrefab(prefab).Reuse(); 20 | } 21 | 22 | public static GameObject Reuse(this GameObject prefab, Transform parent) 23 | { 24 | return Pool.GetPoolByPrefab(prefab).Reuse(parent); 25 | } 26 | 27 | public static GameObject Reuse(this GameObject prefab, Transform parent, bool worldPositionStays) 28 | { 29 | return Pool.GetPoolByPrefab(prefab).Reuse(parent, worldPositionStays); 30 | } 31 | 32 | public static GameObject Reuse(this GameObject prefab, Vector3 position, Quaternion rotation) 33 | { 34 | return Pool.GetPoolByPrefab(prefab).Reuse(position, rotation); 35 | } 36 | 37 | public static GameObject Reuse(this GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) 38 | { 39 | return Pool.GetPoolByPrefab(prefab).Reuse(position, rotation, parent); 40 | } 41 | 42 | public static T Reuse(this GameObject prefab) where T : Component 43 | { 44 | return prefab.Reuse().GetComponent(); 45 | } 46 | 47 | public static T Reuse(this GameObject prefab, Transform parent) where T : Component 48 | { 49 | return prefab.Reuse(parent).GetComponent(); 50 | } 51 | 52 | public static T Reuse(this GameObject prefab, Transform parent, bool worldPositionStays) where T : Component 53 | { 54 | return prefab.Reuse(parent, worldPositionStays).GetComponent(); 55 | } 56 | 57 | public static T Reuse(this GameObject prefab, Vector3 position, Quaternion rotation) where T : Component 58 | { 59 | return prefab.Reuse(position, rotation).GetComponent(); 60 | } 61 | 62 | public static T Reuse(this GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) where T : Component 63 | { 64 | return prefab.Reuse(position, rotation, parent).GetComponent(); 65 | } 66 | 67 | public static void Release(this GameObject instance) 68 | { 69 | var isPooled = Pool.GetPoolByInstance(instance, out var pool); 70 | 71 | if (isPooled) 72 | { 73 | pool.Release(instance); 74 | } 75 | else 76 | { 77 | Object.Destroy(instance); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Runtime/PoolHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a09402c6bbf29ce4eb9f0fa39c83e53f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/PoolInstaller.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace ToolBox.Pools 4 | { 5 | [DefaultExecutionOrder(-9999), DisallowMultipleComponent] 6 | internal sealed class PoolInstaller : MonoBehaviour 7 | { 8 | [SerializeField] private PoolContainer[] _pools; 9 | 10 | private void Awake() 11 | { 12 | for (var i = 0; i < _pools.Length; i++) 13 | _pools[i].Populate(); 14 | } 15 | 16 | [System.Serializable] 17 | private struct PoolContainer 18 | { 19 | [SerializeField] private GameObject _prefab; 20 | [SerializeField, Min(1)] private int _startCount; 21 | 22 | public void Populate() 23 | { 24 | _prefab.Populate(_startCount); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Runtime/PoolInstaller.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4a201a35959c9eb4299beb4d840984af 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Poolable.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | 4 | namespace ToolBox.Pools 5 | { 6 | [DisallowMultipleComponent] 7 | internal sealed class Poolable : MonoBehaviour 8 | { 9 | private IPoolable[] _poolables = Array.Empty(); 10 | private bool _isInitialized; 11 | 12 | private void Awake() 13 | { 14 | _poolables = GetComponentsInChildren(true); 15 | _isInitialized = true; 16 | } 17 | 18 | private void OnDestroy() 19 | { 20 | Pool.Remove(gameObject); 21 | } 22 | 23 | public void OnReuse() 24 | { 25 | if (!_isInitialized) 26 | return; 27 | 28 | for (var i = 0; i < _poolables.Length; i++) 29 | _poolables[i].OnReuse(); 30 | } 31 | 32 | public void OnRelease() 33 | { 34 | for (var i = 0; i < _poolables.Length; i++) 35 | _poolables[i].OnRelease(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Runtime/Poolable.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1d6f0ec84bc54bb4f87128c78eee50fe 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/com.intothedev.objectpooling.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.intothedev.objectpooling", 3 | "rootNamespace": "", 4 | "references": [ 5 | "Zenject" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } 17 | -------------------------------------------------------------------------------- /Runtime/com.intothedev.objectpooling.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2001a717ae7bd9842ab30c2ddd455844 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.intothedev.objectpooling", 3 | "version": "1.8.0", 4 | "displayName": "Object Pooling", 5 | "description": "Object Pooling for Unity.", 6 | "author": { 7 | "name": "IntoTheDev", 8 | "email": "indraayy322@gmail.com", 9 | "url": "https://github.com/IntoTheDev" 10 | }, 11 | "type": "tool" 12 | } 13 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 20b93a3eb6fed5c4a9d75cd501d2e098 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------