Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/mattak/Unidux
Redux Architecture for Unity 🎩
https://github.com/mattak/Unidux
redux unity3d
Last synced: 3 months ago
JSON representation
Redux Architecture for Unity 🎩
- Host: GitHub
- URL: https://github.com/mattak/Unidux
- Owner: mattak
- License: mit
- Created: 2016-05-19T03:08:00.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2021-08-12T23:45:59.000Z (about 3 years ago)
- Last Synced: 2024-07-14T13:35:57.913Z (4 months ago)
- Topics: redux, unity3d
- Language: C#
- Homepage:
- Size: 354 KB
- Stars: 385
- Watchers: 19
- Forks: 27
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
Awesome Lists containing this project
- awesome-unity-open-source-on-github - Unidux - Redux Architecture for Unity (Script Utility)
README
[![Join the chat at https://gitter.im/Unidux/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Unidux/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Unidux is practical application architecture for Unity3D UI.
It's inspired by Redux.
# Install
## UPM
Add following two lines to `Pacakges/manifest.json`.
```
{
"com.neuecc.unirx": "https://github.com/neuecc/UniRx.git?path=Assets/Plugins/UniRx/Scripts",
"me.mattak.unidux": "https://github.com/mattak/Unidux.git?path=Assets/Plugins/Unidux/Scripts",
// ...
}
```## Unity package
No longer supported.
If you need older versions, import unitypackage from [latest releases](https://github.com/mattak/Unidux/releases).# Usage
1) Create your Unidux singleton and place it to unity scene.
```csharp
using UniRx;
using Unidux;public sealed class Unidux : SingletonMonoBehaviour, IStoreAccessor
{
public TextAsset InitialStateJson;private Store _store;
public IStoreObject StoreObject
{
get { return Store; }
}public static State State
{
get { return Store.State; }
}public static Subject Subject
{
get { return Store.Subject; }
}private static State InitialState
{
get
{
return Instance.InitialStateJson != null
? JsonUtility.FromJson(Instance.InitialStateJson.text)
: new State();
}
}public static Store Store
{
get { return Instance._store = Instance._store ?? new Store(InitialState, new Count.Reducer()); }
}public static object Dispatch(TAction action)
{
return Store.Dispatch(action);
}void Update()
{
Store.Update();
}
}
```_Note: `ReplaySubject` is a [ReactiveX concept](http://reactivex.io/documentation/subject.html)
provided by [UniRx](https://github.com/neuecc/UniRx) in this example._2) Create state class to store application state.
```csharp
using System;[Serializable]
public class State : StateBase
{
public int Count = 0;
}
```3) Define action to change state. Define Reducer to move state.
```csharp
public static class Count
{
// specify the possible types of actions
public enum ActionType
{
Increment,
Decrement
}// actions must have a type and may include a payload
public class Action
{
public ActionType ActionType;
}// ActionCreators creates actions and deliver payloads
// in redux, you do not dispatch from the ActionCreator to allow for easy testability
public static class ActionCreator
{
public static Action Create(ActionType type)
{
return new Action() {ActionType = type};
}public static Action Increment()
{
return new Action() {ActionType = ActionType.Increment};
}public static Action Decrement()
{
return new Action() {ActionType = ActionType.Decrement};
}
}// reducers handle state changes
public class Reducer : ReducerBase
{
public override State Reduce(State state, Action action)
{
switch (action.ActionType)
{
case ActionType.Increment:
state.Count++;
break;
case ActionType.Decrement:
state.Count--;
break;
}return state;
}
}
}
```4) Create Renderer to display state and attach it to Text GameObject.
```csharp
[RequireComponent(typeof(Text))]
public class CountRenderer : MonoBehaviour
{
void OnEnable()
{
var text = this.GetComponent();Unidux.Subject
.TakeUntilDisable(this)
.StartWith(Unidux.State)
.Subscribe(state => text.text = state.Count.ToString())
.AddTo(this)
;
}
}
```5) Create dispatcher to update count and attach it to GameObject.
```csharp
[RequireComponent(typeof(Button))]
public class CountDispatcher : MonoBehaviour
{
public Count.Action Action = Count.ActionCreator.Increment();void Start()
{
this.GetComponent()
.OnClickAsObservable()
.Subscribe(state => Unidux.Store.Dispatch(Action))
.AddTo(this)
;
}
}
```That's it!
# Example
- [Counter](Assets/Plugins/Unidux/Examples/Counter)
- [List](Assets/Plugins/Unidux/Examples/List)
- [Todo](Assets/Plugins/Unidux/Examples/Todo)
- [Middlewares](Assets/Plugins/Unidux/Examples/Middlewares)
- [SimpleHttp](Assets/Plugins/Unidux/Examples/SimpleHttp)# Dependencies
- [UniRx](https://github.com/neuecc/UniRx)
- [MiniJSON](https://gist.github.com/darktable/1411710) (for Unidux.Experimental.Editor.StateJsonEditor)# API
## `StateBase`
```csharp
public class State : StateBase
{
public int Count;
}
```### `.Clone()`
```csharp
State _state = new State();
State _clonedState = _state.Clone();
```Create a deep clone of the current state. Useful for Immutability.
## `Store`
```csharp
IReducer[] reducers = new IReducer[]{};
Store _store = new Store(State, reducers);
// State must extend StateBase
```### `.State`
Get the state as passed to the constructor.
### `.Dispatch(object)`
Dispatch an event of `TAction` object,
which will trigger a `Reducer`.### `.ApplyMiddlewares(params Middleware[] middlewares)`
Apply middlewares to Store object
which implement delegate function of [Middleware](Assets/Plugins/Unidux/Scripts/IMiddleware.cs#L5).### `.Update()`
When at least one reducer has been executed,
trigger all the renderers with a copy of the current state.### `.ForceUpdate()`
Trigger all registered renderers with a copy of the current state
regardless of any reducers having been executed.## `SingletonMonoBehaviour`
```csharp
public class Foo : SingletonMonoBehaviour {}
```A singleton base class to extend.
Extends `MonoBehaviour`.### `.Instance`
```csharp
public class Foo : SingletonMonoBehaviour {}Foo.Instance
```The instance of the base class.
# Performance
## `Clone()`
Default implemention of `StateBase.Clone()` is not fast, because it uses `BinaryFormatter` & `MemoryStream`.
And Unidux creates new State on every State chaning (it affects a few milliseconds).
So in case of requiring performance, override clone method with your own logic.e.g.
```csharp
[Serializable]
class State : StateBase
{
public override object Clone()
{
// implement your custom deep clone code
}
}
```## `Equals()`
Default implemention of `StateBase.Equals()` and `StateElement.Equals()` is not fast, because it uses fields and properties reflection.
In case of edit state on UniduxPanel's StateEditor, it calls `Equals()` in order to set `IsStateChanged` flags automatically.
So in case of requiring performance, override `Equals()` method with your own logic.e.g.
```csharp
[Serializable]
class State : StateBase
{
public override bool Equals(object obj)
{
// implement your custom equality check code
}
}
```# Thanks
- [@austinmao](https://github.com/austinmao) for suggestion of Ducks and UniRx.
- [@pine](https://github.com/pine) for description improvement.
- [@jesstelford](https://github.com/jesstelford) for fix document.
- [@tenmihi](https://github.com/tenmihi) for fix document.
- [@kn1cht](https://github.com/kn1cht) for fix .net 4.0 runtime error.
- [@shiena](https://github.com/shiena) for upm support.# License
[MIT](./LICENSE.md)