{"id":23660627,"url":"https://github.com/ishix-g/cmsunivortex","last_synced_at":"2025-12-03T02:30:13.711Z","repository":{"id":243780769,"uuid":"813192556","full_name":"IShix-g/CMSuniVortex","owner":"IShix-g","description":"Data can be retrieved from CMS and imported into ScriptableObject. CMSからデータを取得し、ScriptableObjectにインポート","archived":false,"fork":false,"pushed_at":"2025-01-29T02:50:09.000Z","size":20116,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-29T03:19:34.743Z","etag":null,"topics":["addressables","cockpit-cms","spreadsheets","unity","unity3d"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/IShix-g.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"IShix-g"}},"created_at":"2024-06-10T16:38:23.000Z","updated_at":"2025-01-29T02:49:33.000Z","dependencies_parsed_at":"2024-06-11T05:34:18.968Z","dependency_job_id":"4e02f287-d57a-4624-a0be-2b35b9952cbc","html_url":"https://github.com/IShix-g/CMSuniVortex","commit_stats":null,"previous_names":["ishix-g/cmsunivortex"],"tags_count":52,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IShix-g%2FCMSuniVortex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IShix-g%2FCMSuniVortex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IShix-g%2FCMSuniVortex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IShix-g%2FCMSuniVortex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/IShix-g","download_url":"https://codeload.github.com/IShix-g/CMSuniVortex/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239652969,"owners_count":19675008,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["addressables","cockpit-cms","spreadsheets","unity","unity3d"],"created_at":"2024-12-29T03:59:12.695Z","updated_at":"2025-12-03T02:30:13.666Z","avatar_url":"https://github.com/IShix-g.png","language":"C#","funding_links":["https://github.com/sponsors/IShix-g"],"categories":[],"sub_categories":[],"readme":"[日本語のRead me](docs/README_jp.md)\n\n![Unity](https://img.shields.io/badge/Unity-2021.3%2B-black)\n![Cockpit](https://img.shields.io/badge/Cockpit-v2-black)\n![Google Drive API](https://img.shields.io/badge/GoogleAPI-Drive%20v3%201.68.0.3568-blue)\n![Google Sheet API](https://img.shields.io/badge/GoogleAPI-Sheet%20v4%201.68.0.3574-blue)\n\n# CMSuniVortex\nIt's a plugin that allows you to easily load CMS data into `ScriptableObject`.\n\n![Logo](docs/assets/top.png)\n\n## Table of Contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\u003cdetails\u003e\n\u003csummary\u003eDetails\u003c/summary\u003e\n\n- [Why use this plugin?](#why-use-this-plugin)\n  - [Ease of Input](#ease-of-input)\n  - [High-performance Data](#high-performance-data)\n- [Supported CMS](#supported-cms)\n- [Methods of Reference Supported](#methods-of-reference-supported)\n- [Unity Version](#unity-version)\n- [Getting started](#getting-started)\n  - [Install via git URL](#install-via-git-url)\n- [Quick Start](#quick-start)\n  - [Generate CuvImporter](#generate-cuvimporter)\n  - [Generating the code](#generating-the-code)\n  - [Entering Necessary Information in CuvImporter](#entering-necessary-information-in-cuvimporter)\n    - [Cockpit Client](#cockpit-client)\n  - [Cockpit CMS Test](#cockpit-cms-test)\n    - [Login Information](#login-information)\n    - [Notes for Cockpit Test Server](#notes-for-cockpit-test-server)\n  - [Starting the Import](#starting-the-import)\n  - [Specifying the Output](#specifying-the-output)\n  - [Retrieval and Display of Data](#retrieval-and-display-of-data)\n- [Setup for Cockpit](#setup-for-cockpit)\n- [Roles of Each Class](#roles-of-each-class)\n- [Why Do I Want to Make This Plugin?](#why-do-i-want-to-make-this-plugin)\n  - [1. Addressable](#1-addressable)\n    - [Advantages](#advantages)\n    - [Concerns](#concerns)\n  - [2. WebView](#2-webview)\n    - [Merits](#merits)\n    - [Concerns](#concerns-1)\n  - [3. Json](#3-json)\n    - [Merits](#merits-1)\n    - [Concerns](#concerns-2)\n  - [What We Learned From the Test Results](#what-we-learned-from-the-test-results)\n    - [iOS : iPhone SE2 17.5.1](#ios--iphone-se2-1751)\n    - [Android : Galaxy S10 Android11](#android--galaxy-s10-android11)\n- [Plans for the Future](#plans-for-the-future)\n\n\u003c/details\u003e\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Why use this plugin?\n\nThis plugin is built on the concept of being **\"Easy to input and delivering top-notch performance\"**.\n\n### Ease of Input\n\nWhen it comes to input, we think of CMS. CMS is filled with knowledge on **how to input data easily and without stress**. Also, it gives the ease of being able to be updated from anywhere.\n\n### High-performance Data\n\n`ScriptableObject` is a data format that is optimized for handling in Unity. It has excellent performance.\n\nHowever, these two may seem unrelated at first glance. But it is CMSuniVortex that connects CMS and `ScriptableObject`. This is not a mere plugin, but a solution born out of pursuing efficiency and performance.\n\n## Supported CMS\n\n- [Cockpit](docs/IntegrationWithCockpit.md)\n- [Google Sheets](docs/IntegrationWithGoogleSheet.md)\n\n## Methods of Reference Supported\n\nYou can specify how to refer to the data you have output.\n\n- Direct reference\n- Referenced via [Addressables](https://docs.unity3d.com/Packages/com.unity.addressables@1.19/manual/index.html)\n\n## Unity Version\nUnity 2021.3.x or higher\n\n## Getting started\n\n### Install via git URL\nPlease add the URL to \"Window \u003e Package Manager \u003e Add package from git URL...\".\n\nURL: `https://github.com/IShix-g/CMSuniVortex.git?path=Packages/CMSuniVortex`\n\n![Package Manager](docs/assets/package_manager.png)\n\n## Quick Start\n\n### Generate CuvImporter\n\nRight click on the Project and select \"Create \u003e CMSuniVortex \u003e create CuvImporter\" to create a `CuvImporter`.\n\n![create](docs/assets/create.png)\n\n### Generating the code\n\nClick the \"Script Generator\" button on the generated `CuvImporter`.\n\n\u003cimg alt=\"open generator\" src=\"docs/assets/open_generator.png\" width=\"600\"/\u003e\n\nEnter the necessary information to generate the code. In this case, we generate the code for Cockpit.\n\n\u003cimg alt=\"create classes\" src=\"docs/assets/create_classes.png\" width=\"600\"/\u003e\n\n|                 | explanation                               | e.g.                |\n|-----------------|-------------------------------------------|---------------------|\n| Full Class Name | Specify the class name. Namespace can also be specified. | namespace.ClassName |\n| Build Path      | Specify the path of the directory to generate the code.      | Assets/Models/      |\n\n\n### Entering Necessary Information in CuvImporter\n\nAfter generating, return to CuvImporter and enter the necessary information. Specify the script generated earlier as the client. This time, we selected `CatDetailsCockpitCuvClient` for direct reference. If using [Addressables](https://docs.unity3d.com/Packages/com.unity.addressables@1.19/manual/index.html), select the `AddressableClient` above it.\n\n**Naming rule for output Client:** \"Full class name specified when generating code\" + \"CMS name\" + \"Output name\" + \"CuvClient\"\n\n|            | explanation                                            | e.g.           |\n|------------|--------------------------------------------------------|----------------|\n| Build Path | Specify the directory where the data will be output.           | Assets/Models/ |\n| Languages  | Specify the language, even if not used, at least one needs to be selected.    | English|\n| Client     | Specify any client for direct reference or Addressables, etc.. | Test.ClassNameCockpitCuvClient|\n| Output     | Decide how to refer to the data output by the client. | Test.ClassNameCockpitCuvOutput|\n\n\u003cimg alt=\"select client\" src=\"docs/assets/select_client.png\" width=\"600\"/\u003e\n\n#### Cockpit Client\n\n|            | explanation                                            | e.g.           |\n|------------|--------------------------------------------------------|----------------|\n| Base Url | URL where Cockpit is installed           | https://xxx.xxx.com/cockpit/ |\n| Api Key  | Api Key obtainable from the Cockpit admin page    | English|\n| Model Name      | Model name set on Cockpit's admin page | Model |\n\n### Cockpit CMS Test\nActual tests using Cockpit CMS are possible. Please use the following.\n\n|            | value                                        |\n|------------|----------------------------------------------|\n| Base Url   | [https://devx.myonick.biz/cockpit/](https://devx.myonick.biz/cockpit/)|\n| Api Key    | API-a92fac21986ac045e143f07c27c60e09f19ae856 |\n| Model Name | Model                                        |\n\n#### Login Information\n\nAlthough the permission is read-only, you can actually log in and view the admin page.\n\n|     | value                                                                  |\n|-----|------------------------------------------------------------------------|\n| URL | [https://devx.myonick.biz/cockpit/](https://devx.myonick.biz/cockpit/) |\n| ID  | guest                                                                  |\n| PW  | guest                                                                  |\n\n#### Notes for Cockpit Test Server\n\n- Please use in moderation.\n- Do not access too frequently.\n- Do not perform consecutive imports.\n- Although advertisements are displayed because I use a free rental server, I am not involved at all.\n- Please note that we may stop without notice if we find inappropriate access.\n\n### Starting the Import\n\nAfter input, click Import, and the data is generated in the specified directory.\n\n\u003cimg alt=\"start import\" src=\"docs/assets/start_import.png\" width=\"600\"/\u003e\n\n\n### Specifying the Output\n\nDecide how to refer to the imported data. This time, we select `CatDetailsCockpitCuvOutput` for direct reference.\n\n\u003cimg alt=\"start import\" src=\"docs/assets/select_output.png\" width=\"600\"/\u003e\n\nAfter selection, click on Output to generate it.\n\n\u003cimg alt=\"start import\" src=\"docs/assets/start_output.png\" width=\"600\"/\u003e\n\n### Retrieval and Display of Data\n\nData can be retrieved using `GetList()` from the generated `CatDetailsCockpitCuvReference`. If you use the prepared `CuvComponent`, you can retrieve it as follows.\n\n\u003cimg alt=\"start import\" src=\"docs/assets/test_text.png\" width=\"600\"/\u003e\n\nThe instance of Reference and the Key set on the inspector are passed, so you use `TryGetByKey` to retrieve it.\n\n```csharp\nusing UnityEngine;\nusing UnityEngine.UI;\nusing CMSuniVortex.Compornents;\n\npublic sealed class TestText : CuvComponent\u003cCatDetailsCockpitCuvReference\u003e\n{\n    [SerializeField] Text _text;\n    \n    protected override void OnChangeLanguage(CatDetailsCockpitCuvReference reference, string key)\n    {\n        if (reference.GetList().TryGetByKey(key, out var model))\n        {\n            _text.text = model.Text;\n        }\n    }\n}\n```\n\n※ `CuvAsyncComponent` is used for Addressables.\n\n## Setup for Cockpit\n\nFor details on how to set up, please see [here](docs/IntegrationWithCockpit.md).\n\n\n## Roles of Each Class\n\nYou can check representative classes that constitute the plugin [here](docs/RelationshipsBetweenClasses.md).\n\n## Why Do I Want to Make This Plugin?\n\nWhat prompted me to develop this plugin was performance testing. There are roughly three methods to download data and display it.\n\n### 1. Addressable\n\n#### Advantages\n\nGood performance with no need for deserialization or data conversion by using `ScriptableObject` or `Sprite`\n\n#### Concerns\n\nSince it needs to be exported by Unity, a programmer is required. Or a significant conversion system needs to be established.\n\n\u003cdetails\u003e\u003csummary\u003eTest Code\u003c/summary\u003e\n\n\n```csharp\n\nusing UnityEngine;\nusing UnityEngine.UI;\nusing UnityEngine.AddressableAssets;\nusing UnityEngine.ResourceManagement.AsyncOperations;\nusing UnityEngine.Profiling;\n\npublic sealed class AddressableTest : MonoBehaviour\n{\n    [SerializeField] Image _image;\n    [SerializeField] Text _text;\n    [SerializeField] Button _loadButton;\n    [SerializeField] Button _unloadButton;\n\n    AsyncOperationHandle\u003cAddressableData\u003e _handle;\n    \n    void Start()\n    {\n        _loadButton.onClick.AddListener(OnLoadButtonClicked);\n        _unloadButton.onClick.AddListener(OnUnloadButtonClicked);\n        _loadButton.interactable = true;\n        _unloadButton.interactable = false;\n    }\n\n    void OnDestroy() =\u003e Unload();\n\n    async void OnLoadButtonClicked()\n    {\n        _loadButton.interactable = false;\n        _unloadButton.interactable = true;\n        \n        Profiler.BeginSample(\"AddressableTestProfile1\");\n        _handle = Addressables.LoadAssetAsync\u003cAddressableData\u003e(\"AddressableData\");\n        Profiler.EndSample();\n        await _handle.Task;\n        \n        Profiler.BeginSample(\"AddressableTestProfile2\");\n        var obj = _handle.Result;\n        _image.sprite = obj.Image;\n        _text.text = obj.GetText();\n        Profiler.EndSample();\n    }\n\n    void OnUnloadButtonClicked()\n    {\n        Unload();\n        _loadButton.interactable = true;\n        _unloadButton.interactable = false;\n    }\n\n    void Unload()\n    {\n        if (_image != default)\n        {\n            _image.sprite = default;\n        }\n        if (_text != default)\n        {\n            _text.text = default;\n        }\n        \n        if (_handle.IsValid())\n        {\n            Addressables.Release(_handle);\n        }\n    }\n}\n\n[CreateAssetMenu(fileName = \"AddressableData\", menuName = \"ScriptableObject/AddressableData\", order = 0)]\npublic sealed class AddressableData : ScriptableObject\n{\n    public int ID;\n    public string Title;\n    public string Contents;\n    public Sprite Image;\n\n    public string GetText() =\u003e \"ID:\" + ID + \"\\nTitle:\" + Title + \"\\nContents:\" + Contents;\n}\n```\n\n\u003c/details\u003e\n\n\n### 2. WebView\n\nWebview from [Cross Platform Essential Kit](https://assetstore.unity.com/packages/tools/integration/cross-platform-native-plugins-essential-kit-mobile-ios-android-140111)\n\n#### Merits\n\n- Can be used for both WEB pages and applications.\n- The layout can be free even after release.\n\n#### Concerns\n\n- Concerned about the amount of memory used.\n\n\u003cdetails\u003e\u003csummary\u003eTest Code\u003c/summary\u003e\n\n```csharp\n\nusing UnityEngine;\nusing UnityEngine.UI;\nusing UnityEngine.Profiling;\nusing VoxelBusters.CoreLibrary;\nusing VoxelBusters.EssentialKit;\n\npublic sealed class WebViewTest : MonoBehaviour\n{\n    const string url = \"https://xxx.xxxx.com/webview/\";\n    \n    [SerializeField] Button _openButton;\n    [SerializeField] Button _closeButton;\n\n    WebView _webView;\n\n    void Start()\n    {\n        _openButton.onClick.AddListener(ClickOpenButton);\n        _closeButton.onClick.AddListener(ClickCloseButton);\n        _openButton.interactable = true;\n        _closeButton.interactable = false;\n    }\n\n    void OnEnable()\n    {\n        WebView.OnShow += OnWebViewShow;\n        WebView.OnHide += OnWebViewHide;\n    }\n    \n    void OnDisable()\n    {\n        WebView.OnShow -= OnWebViewShow;\n        WebView.OnHide -= OnWebViewHide;\n    }\n    \n    void ClickOpenButton()\n    {\n        _openButton.interactable = false;\n        \n        Profiler.BeginSample(\"WebViewTestProfile\");\n        _webView = WebView.CreateInstance();\n        _webView.SetNormalizedFrame(new Rect(0.1f, 0.2f, 0.8f, 0.6f));\n        _webView.LoadURL(URLString.URLWithPath(url));\n        _webView.Show();\n        Profiler.EndSample();\n    }\n    \n    void ClickCloseButton() =\u003e _webView.Hide();\n    \n    void OnWebViewShow(WebView view) =\u003e _closeButton.interactable = true;\n\n    void OnWebViewHide(WebView view)\n    {\n        _openButton.interactable = true;\n        _closeButton.interactable = false;\n    }\n}\n```\n\nWebページ\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"ja\"\u003e\n\u003chead\u003e\n    \u003cmeta charset=\"utf-8\"\u003e\n    \u003ctitle\u003eTest\u003c/title\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"\u003e\n    \u003cmeta name=\"format-detection\" content=\"telephone=no,email=no,address=no\"\u003e\n    \u003cstyle type=\"text/css\"\u003e\n        img{\n            max-width: 100%;\n        }\n    \u003c/style\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\n\u003cdiv id=\"myData\"\u003e\n    \u003ch2 id=\"title\"\u003e\u003c/h2\u003e\n    \u003cp id=\"contents\"\u003e\u003c/p\u003e\n    \u003cimg id=\"image\" src=\"\" alt=\"Image\"\u003e\n\u003c/div\u003e\n\n\u003cscript src=\"https://code.jquery.com/jquery-1.12.4.min.js\"\u003e\u003c/script\u003e\n\n\u003cscript\u003e\n    $.ajax({\n        url: 'getModel.php',\n        dataType: 'json',\n        success: function(data) {\n            $('#title').text(data.Title);\n            $('#contents').text(data.Contents);\n            $('#image').attr('src', data.Image);\n        },\n        error: function (request, status, error) {\n            console.log(\"Error: Could not fetch data\");\n        }\n    });\n\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nデータを取得するAPI\n```php\n\u003c?php\n\nclass Model {\n    public $Id;\n    public $Title;\n    public $Contents;\n    public $Image;\n}\n\nmb_language(\"uni\");\nmb_internal_encoding(\"UTF-8\");\nheader('Content-type: application/json');\n\n$model = new Model();\n$model-\u003eId = 2222;\n$model-\u003eTitle = '猫　ねこ';\n$model-\u003eContents = '猫は、古代のミアキスと言う豹のような大きな動物が起源と言われています。 今から４０００～５０００年前にエジプトから発生し、住み良い環境を求め分化して中東に行きました。';\n$model-\u003eImage = 'https://xxx.xxxx.com/webview/cat.jpg';\necho json_encode( $model );\n```\n\n\u003c/details\u003e\n\n\n### 3. Json\n\nDisplay by converting JSON obtained from the server using [UnityWebRequest](https://docs.unity3d.com/ja/2021.3/ScriptReference/Networking.UnityWebRequest.html).\n\n#### Merits\n- Can be used for WEB and apps.\n- Apart from initialization, it seems lighter than WebView.\n\n#### Concerns\n- There is a concern about the initialization cost if there are many images (only one image in the test).\n- If you don't cache data, you need to provide your own caching mechanism.\n\n\u003cdetails\u003e\u003csummary\u003eTest Code\u003c/summary\u003e\n\n```csharp\n\nusing System;\nusing System.Collections;\nusing UnityEngine;\nusing UnityEngine.UI;\nusing UnityEngine.Networking;\nusing UnityEngine.Profiling;\n\npublic sealed class JsonTest : MonoBehaviour\n{\n    const string apiUrl = \"https://xxx.xxxx.com/webview/getModel.php\";\n    \n    [SerializeField] Image _image;\n    [SerializeField] Text _text;\n    [SerializeField] Button _loadButton;\n    [SerializeField] Button _unloadButton;\n    \n    [Serializable]\n    sealed class Model\n    {\n        public int ID;\n        public string Title;\n        public string Contents;\n        public string Image;\n        \n        public string GetText() =\u003e \"ID:\" + ID + \"\\nTitle:\" + Title + \"\\nContents:\" + Contents;\n    }\n\n    void Start()\n    {\n        _loadButton.onClick.AddListener(OnLoadButtonClicked);\n        _unloadButton.onClick.AddListener(OnUnloadButtonClicked);\n        _loadButton.interactable = true;\n        _unloadButton.interactable = false;\n    }\n\n    void OnDestroy() =\u003e Unload();\n        \n    void OnLoadButtonClicked()\n    {\n        _loadButton.interactable = false;\n        _unloadButton.interactable = false;\n        \n        StartCoroutine(LoadCo((model, sprite) =\u003e\n        {\n            _text.text = model.GetText();\n            _image.sprite = sprite;\n            Profiler.EndSample();\n            _unloadButton.interactable = true;\n        }));\n    }\n    \n    IEnumerator LoadCo(Action\u003cModel, Sprite\u003e onSuccess)\n    {\n        Profiler.BeginSample(\"JsonTestProfile1\");\n        using var request = UnityWebRequest.Get(apiUrl);\n        Profiler.EndSample();\n        yield return request.SendWebRequest();\n        \n        if (request.result == UnityWebRequest.Result.Success)\n        {\n            Profiler.BeginSample(\"JsonTestProfile2\");\n            var model = JsonUtility.FromJson\u003cModel\u003e(request.downloadHandler.text);\n            using var imgRequest = UnityWebRequestTexture.GetTexture(model.Image);\n            Profiler.EndSample();\n            yield return imgRequest.SendWebRequest();\n            \n            if (imgRequest.result == UnityWebRequest.Result.Success)\n            {\n                Profiler.BeginSample(\"JsonTestProfile3\");\n                var texture = ((DownloadHandlerTexture)imgRequest.downloadHandler).texture;\n                var sprite = Sprite.Create(\n                    texture, \n                    new Rect(0, 0, texture.width, texture.height), \n                    new Vector2(0.5f, 0.5f));\n                \n                onSuccess?.Invoke(model, sprite);\n            }\n            else\n            {\n                Debug.LogError(imgRequest.error);\n            }\n        }\n        else\n        {\n            Debug.LogError(request.error);\n        }\n    }\n    \n    void OnUnloadButtonClicked()\n    {\n        Unload();\n        _loadButton.interactable = true;\n        _unloadButton.interactable = false;\n    }\n\n    void Unload()\n    {\n        if (_image != default\n            \u0026\u0026 _image.sprite != default)\n        {\n            var tex = _image.sprite.texture;\n            _image.sprite = null;\n            DestroyImmediate(tex);\n            Resources.UnloadUnusedAssets();\n        }\n        if (_text != default)\n        {\n            _text.text = default;\n        }\n    }\n}\n```\n\n\u003c/details\u003e\n\n### What We Learned From the Test Results\n\nFrom these tests, we learned that:\n\n- Addressable performs the best.\n- WebView uses significant memory on Android. It might not be possible to fully release all memory.\n- Json has a significant initialization cost when there are many images.\n\nFrom these results, I wanted to use Addressable, which has the best performance, but also allows for easy updates from the CMS, so I developed this plugin.\n\n#### iOS : iPhone SE2 17.5.1\n\n|  | GC Alloc | Time | Size |\n|---|:--|:--|---|\n| Addressables | 3.2KB | 0.24ms | 1.1MB |\n| WebView | 22.9KB | 0.52ms | 2MB |\n| Json | 15KB | 3.75ms | 2.3MB |\n\n#### Android : Galaxy S10 Android11\n\n|  | GC Alloc | Time | Size |\n|---|:--|:--|---|\n| Addressables | 3.1KB | 0.24ms | 9MB |\n| WebView | 31.8KB | 0.56ms | 70MB |\n| Json | 4.3KB | 1.18ms | 9.7MB |\n\n## Plans for the Future\n\nCurrently, the system only supports up to the generation of `ScriptableObject`. However, we are planning to enhance it to handle the locally generated objects and to build Addressable to send to a server. We would also like to increase support for CMS. If you are interested, we appreciate your cooperation.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fishix-g%2Fcmsunivortex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fishix-g%2Fcmsunivortex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fishix-g%2Fcmsunivortex/lists"}