Redux Architecture for Unity 🎩
C#
Latest commit 52809d1 Jan 30, 2017 @mattak committed on GitHub Merge pull request #70 from mattak#56
Issues/56 Discard onetime value support
Permalink
Failed to load latest commit information.
Assets issues/56 design(Example): update background color of example Jan 30, 2017
ProjectSettings issues/52 chore(Unity3d): version up Unity 5.5.0 Dec 23, 2016
art doc(README): logo added Jul 19, 2016
submodule
.gitignore
.gitmodules
CHANGELOG.md
LICENSE.md
README.md

README.md

Join the chat at https://gitter.im/Unidux/Lobby

Unidux is practical application architecture for Unity3D UI.

It's inspired by Redux.

Install

Import unitypackage from latest releases.

Usage

1) Create your Unidux singleton and place it to unity scene.

using UniRx;
using Unidux;

public sealed partial class Unidux : SingletonMonoBehaviour<Unidux>
{
    partial void AddReducers(Store<State> store);

    private ReplaySubject<State> _subject;
    private Store<State> _store;
    private State _state;

    public static State State
    {
        get { return Instance._state = Instance._state ?? new State(); }
    }

    public static ReplaySubject<State> Subject
    {
        get { return Instance._subject = Instance._subject ?? new ReplaySubject<State>(); }
    }

    public static Store<State> Store
    {
        get
        {
            if (Instance._store == null)
            {
                Instance._store = new Store<State>(State);
                Instance._store.AddRenderer(state => Subject.OnNext(state));
                Instance.AddReducers(Instance._store);
            }
            return Instance._store;
        }
    }

    void Update()
    {
        Store.Update();
    }
}

public sealed partial class Unidux
{
    partial void AddReducers(Store<State> store)
    {
        store.AddReducer<Count.ActionType>(Count.Reducer);
    }
}

Note: ReplaySubject is a ReactiveX concept provided by UniRx in this example.

2) Create state class to store application state.

using Unidux;
public class State : StateBase<State>
{
    public int Count { get; set; }
}

3) Define action to change state. Define Reducer to move state.

public static class Count
{
    public enum ActionType
    {
        Increment,
        Decrement
    }

    public class Action
    {
        public ActionType ActionType;
    }

    public static State Reducer(State state, ActionType action)
    {
        switch (action)
        {
            case ActionType.Increment:
                state.Count++;
                break;
            case ActionType.Decrement:
                state.Count--;
                break;
        }

        return state;
    }

    public static class ActionCreator
    {
        public static Action Increment()
        {
            return new Action() {ActionType = ActionType.Increment};
        }

        public static Action Decrement()
        {
            return new Action() {ActionType = ActionType.Decrement};
        }
    }
}

4) Create Renderer to display state and attach it to Text GameObject.

[RequireComponent(typeof(Text))]
public class CountRenderer : MonoBehaviour
{
    void OnEnable()
    {
        var text = this.GetComponent<Text>();

        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.

[RequireComponent(typeof(Button))]
public class CountDispatcher : MonoBehaviour
{
    public Count.ActionType ActionType = Count.ActionType.Increment;

    void Start()
    {
        this.GetComponent<Button>()
            .OnClickAsObservable()
            .Subscribe(state => Unidux.Store.Dispatch(ActionType))
            .AddTo(this)
            ;
    }
}

That's it!

Example

Dependencies

  • UniRx is refered on Example

API

StateBase

public class State : StateBase<State>
{
    public int Count { get; set; }
}

<StateBase>.Clone()

State _state = new State();
State _clonedState = _state.Clone();

Create a deep clone of the current state. Useful for Immutability.

Store

Store _store = new Store<State>(State);
// State must extend StateBase

<Store>.State

Get the state as passed to the constructor.

<Store>.AddReducer<TAction>(<TReducer>)

Add a Reducer which handles events of type TAction. Only one reducer per type is allowed.

Where TReducer is a method which conforms to Reducer.

<Store>.RemoveReducer<TAction>(<TReducer>)

Remove a previously added reducer from the store.

Where TReducer is a method which conforms to Reducer.

<Store>.AddRenderer(<TRenderer>)

Add a Renderer to the store. Multiple renderers can be added based on uniqueness of .GetHashCode()

Where TRenderer is a method which conforms to Renderer.

<Store>.RemoveRenderer(<TRenderer>)

Remove a previously added renderer from the store.

Where TRenderer is a method which conforms to Renderer.

<Store>.Dispatch<TAction>(<TAction>)

Dispatch an event of type TAction, which will trigger a Reducer<TAction>.

<Store>.Update()

When at least one reducer has been executed, trigger all the renderers with a copy of the current state.

<Store>.ForceUpdate()

Trigger all registered renderers with a copy of the current state regardless of any reducers having been executed.

SingletonMonoBehaviour

public class Foo : SingletonMonoBehaviour<Foo> {}

A singleton base class to extend. Extends MonoBehaviour.

<SingletonMonoBehaviour>.Instance

public class Foo : SingletonMonoBehaviour<Foo> {}

Foo.Instance

The instance of the base class.

Thanks

License

MIT