{"id":13786297,"url":"https://github.com/mattak/Unidux","last_synced_at":"2025-05-11T22:30:49.348Z","repository":{"id":47801998,"uuid":"59169633","full_name":"mattak/Unidux","owner":"mattak","description":"Redux Architecture for Unity 🎩","archived":false,"fork":false,"pushed_at":"2021-08-12T23:45:59.000Z","size":362,"stargazers_count":387,"open_issues_count":12,"forks_count":27,"subscribers_count":19,"default_branch":"master","last_synced_at":"2024-11-17T22:36:15.091Z","etag":null,"topics":["redux","unity3d"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mattak.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-05-19T03:08:00.000Z","updated_at":"2024-11-15T08:52:56.000Z","dependencies_parsed_at":"2022-09-10T06:51:27.920Z","dependency_job_id":null,"html_url":"https://github.com/mattak/Unidux","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattak%2FUnidux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattak%2FUnidux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattak%2FUnidux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattak%2FUnidux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mattak","download_url":"https://codeload.github.com/mattak/Unidux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253645098,"owners_count":21941311,"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":["redux","unity3d"],"created_at":"2024-08-03T19:01:13.308Z","updated_at":"2025-05-11T22:30:48.133Z","avatar_url":"https://github.com/mattak.png","language":"C#","funding_links":[],"categories":["Script Utility","Game Development"],"sub_categories":["Unity Engine: Resources"],"readme":"# \u003ca href=\"https://github.com/mattak/Unidux\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/mattak/Unidux/master/art/unidux-logo-horizontal.png\" height=\"60\"\u003e\u003c/a\u003e\n\n[![Join the chat at https://gitter.im/Unidux/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Unidux/Lobby?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\nUnidux is practical application architecture for Unity3D UI.\n\nIt's inspired by Redux.\n\n# Install \n\n## UPM \n\nAdd following two lines to `Pacakges/manifest.json`.\n\n```\n{\n \"com.neuecc.unirx\": \"https://github.com/neuecc/UniRx.git?path=Assets/Plugins/UniRx/Scripts\",\n \"me.mattak.unidux\": \"https://github.com/mattak/Unidux.git?path=Assets/Plugins/Unidux/Scripts\",\n // ...\n}\n```\n\n## Unity package\n\nNo longer supported.\nIf you need older versions, import unitypackage from [latest releases](https://github.com/mattak/Unidux/releases).\n\n# Usage\n\n1) Create your Unidux singleton and place it to unity scene.\n\n```csharp\nusing UniRx;\nusing Unidux;\n\npublic sealed class Unidux : SingletonMonoBehaviour\u003cUnidux\u003e, IStoreAccessor\n{\n    public TextAsset InitialStateJson;\n\n    private Store\u003cState\u003e _store;\n\n    public IStoreObject StoreObject\n    {\n        get { return Store; }\n    }\n\n    public static State State\n    {\n        get { return Store.State; }\n    }\n\n    public static Subject\u003cState\u003e Subject\n    {\n        get { return Store.Subject; }\n    }\n\n    private static State InitialState\n    {\n        get\n        {\n            return Instance.InitialStateJson != null\n                ? JsonUtility.FromJson\u003cState\u003e(Instance.InitialStateJson.text)\n                : new State();\n        }\n    }\n\n    public static Store\u003cState\u003e Store\n    {\n        get { return Instance._store = Instance._store ?? new Store\u003cState\u003e(InitialState, new Count.Reducer()); }\n    }\n\n    public static object Dispatch\u003cTAction\u003e(TAction action)\n    {\n        return Store.Dispatch(action);\n    }\n\n    void Update()\n    {\n        Store.Update();\n    }\n}\n```\n\n_Note: `ReplaySubject` is a [ReactiveX concept](http://reactivex.io/documentation/subject.html)\nprovided by [UniRx](https://github.com/neuecc/UniRx) in this example._\n\n2) Create state class to store application state.\n\n```csharp\nusing System;\n\n[Serializable]\npublic class State : StateBase\n{\n    public int Count = 0;\n}\n```\n\n3) Define action to change state. Define Reducer to move state.\n\n```csharp\npublic static class Count\n{\n    // specify the possible types of actions\n    public enum ActionType\n    {\n        Increment,\n        Decrement\n    }\n\n    // actions must have a type and may include a payload\n    public class Action\n    {\n        public ActionType ActionType;\n    }\n\n    // ActionCreators creates actions and deliver payloads\n    // in redux, you do not dispatch from the ActionCreator to allow for easy testability\n    public static class ActionCreator\n    {\n        public static Action Create(ActionType type)\n        {\n            return new Action() {ActionType = type};\n        }\n\n        public static Action Increment()\n        {\n            return new Action() {ActionType = ActionType.Increment};\n        }\n\n        public static Action Decrement()\n        {\n            return new Action() {ActionType = ActionType.Decrement};\n        }\n    }\n\n    // reducers handle state changes\n    public class Reducer : ReducerBase\u003cState, Action\u003e\n    {\n        public override State Reduce(State state, Action action)\n        {\n            switch (action.ActionType)\n            {\n                case ActionType.Increment:\n                    state.Count++;\n                    break;\n                case ActionType.Decrement:\n                    state.Count--;\n                    break;\n            }\n\n            return state;\n        }\n    }\n}\n```\n\n4) Create Renderer to display state and attach it to Text GameObject.\n\n```csharp\n[RequireComponent(typeof(Text))]\npublic class CountRenderer : MonoBehaviour\n{\n    void OnEnable()\n    {\n        var text = this.GetComponent\u003cText\u003e();\n\n        Unidux.Subject\n            .TakeUntilDisable(this)\n            .StartWith(Unidux.State)\n            .Subscribe(state =\u003e text.text = state.Count.ToString())\n            .AddTo(this)\n            ;\n    }\n}\n```\n\n5) Create dispatcher to update count and attach it to GameObject.\n\n```csharp\n[RequireComponent(typeof(Button))]\npublic class CountDispatcher : MonoBehaviour\n{\n    public Count.Action Action = Count.ActionCreator.Increment();\n\n    void Start()\n    {\n        this.GetComponent\u003cButton\u003e()\n            .OnClickAsObservable()\n            .Subscribe(state =\u003e Unidux.Store.Dispatch(Action))\n            .AddTo(this)\n            ;\n    }\n}\n```\n\nThat's it!\n\n\n# Example\n\n- [Counter](Assets/Plugins/Unidux/Examples/Counter)\n- [List](Assets/Plugins/Unidux/Examples/List)\n- [Todo](Assets/Plugins/Unidux/Examples/Todo)\n- [Middlewares](Assets/Plugins/Unidux/Examples/Middlewares)\n- [SimpleHttp](Assets/Plugins/Unidux/Examples/SimpleHttp)\n\n# Dependencies\n\n- [UniRx](https://github.com/neuecc/UniRx)\n- [MiniJSON](https://gist.github.com/darktable/1411710) (for Unidux.Experimental.Editor.StateJsonEditor)\n\n# API\n\n## `StateBase`\n\n```csharp\npublic class State : StateBase\n{\n    public int Count;\n}\n```\n\n### `\u003cStateBase\u003e.Clone()`\n\n```csharp\nState _state = new State();\nState _clonedState = _state.Clone();\n```\n\nCreate a deep clone of the current state. Useful for Immutability.\n\n## `Store`\n\n```csharp\nIReducer[] reducers = new IReducer[]{};\nStore _store = new Store\u003cState\u003e(State, reducers);\n// State must extend StateBase\n```\n\n### `\u003cStore\u003e.State`\n\nGet the state as passed to the constructor.\n\n### `\u003cStore\u003e.Dispatch(object)`\n\nDispatch an event of `TAction` object,\nwhich will trigger a `Reducer\u003cTAction\u003e`.\n\n### `\u003cStore\u003e.ApplyMiddlewares(params Middleware[] middlewares)`\n\nApply middlewares to Store object\nwhich implement delegate function of [Middleware](Assets/Plugins/Unidux/Scripts/IMiddleware.cs#L5).\n\n### `\u003cStore\u003e.Update()`\n\nWhen at least one reducer has been executed,\ntrigger all the renderers with a copy of the current state.\n\n### `\u003cStore\u003e.ForceUpdate()`\n\nTrigger all registered renderers with a copy of the current state\nregardless of any reducers having been executed.\n\n## `SingletonMonoBehaviour`\n\n```csharp\npublic class Foo : SingletonMonoBehaviour\u003cFoo\u003e {}\n```\n\nA singleton base class to extend.\nExtends `MonoBehaviour`.\n\n### `\u003cSingletonMonoBehaviour\u003e.Instance`\n\n```csharp\npublic class Foo : SingletonMonoBehaviour\u003cFoo\u003e {}\n\nFoo.Instance\n```\n\nThe instance of the base class.\n\n# Performance\n\n## `Clone()`\n\nDefault implemention of `StateBase.Clone()` is not fast, because it uses `BinaryFormatter` \u0026 `MemoryStream`.\nAnd Unidux creates new State on every State chaning (it affects a few milliseconds).\nSo in case of requiring performance, override clone method with your own logic.\n\ne.g.\n\n```csharp\n[Serializable]\nclass State : StateBase\n{\n    public override object Clone()\n    {\n        // implement your custom deep clone code\n    }\n}\n```\n\n## `Equals()`\n\nDefault implemention of `StateBase.Equals()` and `StateElement.Equals()` is not fast, because it uses fields and properties reflection.\nIn case of edit state on UniduxPanel's StateEditor, it calls `Equals()` in order to set `IsStateChanged` flags automatically.\nSo in case of requiring performance, override `Equals()` method with your own logic.\n\ne.g.\n\n```csharp\n[Serializable]\nclass State : StateBase\n{\n    public override bool Equals(object obj)\n    {\n        // implement your custom equality check code\n    }\n}\n```\n\n# Thanks\n\n- [@austinmao](https://github.com/austinmao) for suggestion of Ducks and UniRx.\n- [@pine](https://github.com/pine) for description improvement.\n- [@jesstelford](https://github.com/jesstelford) for fix document.\n- [@tenmihi](https://github.com/tenmihi) for fix document.\n- [@kn1cht](https://github.com/kn1cht) for fix .net 4.0 runtime error.\n- [@shiena](https://github.com/shiena) for upm support.\n\n# License\n\n[MIT](./LICENSE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattak%2FUnidux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmattak%2FUnidux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattak%2FUnidux/lists"}