{"id":14960445,"url":"https://github.com/aleshmandr/mace","last_synced_at":"2025-06-28T18:33:17.635Z","repository":{"id":221822805,"uuid":"755469534","full_name":"Aleshmandr/Mace","owner":"Aleshmandr","description":"Mace is an MVVM framework for Unity with editor-assignable bindings","archived":false,"fork":false,"pushed_at":"2025-05-13T14:52:59.000Z","size":664,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-13T15:55:10.628Z","etag":null,"topics":["archicture","binding","bindings","editor","mvvm","package","tools","ui","unity","unity-package","unity3d","unity3d-plugin"],"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/Aleshmandr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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,"zenodo":null}},"created_at":"2024-02-10T10:01:34.000Z","updated_at":"2025-05-13T14:53:03.000Z","dependencies_parsed_at":"2024-07-27T14:49:00.321Z","dependency_job_id":"5e6cbd9e-eaaf-4c67-9101-f0fde3f78602","html_url":"https://github.com/Aleshmandr/Mace","commit_stats":{"total_commits":49,"total_committers":1,"mean_commits":49.0,"dds":0.0,"last_synced_commit":"b6f5d002f4f3c315909b19bcc3c87d79880527d6"},"previous_names":["aleshmandr/mace"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Aleshmandr/Mace","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aleshmandr%2FMace","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aleshmandr%2FMace/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aleshmandr%2FMace/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aleshmandr%2FMace/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Aleshmandr","download_url":"https://codeload.github.com/Aleshmandr/Mace/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aleshmandr%2FMace/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262476542,"owners_count":23317303,"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":["archicture","binding","bindings","editor","mvvm","package","tools","ui","unity","unity-package","unity3d","unity3d-plugin"],"created_at":"2024-09-24T13:22:18.970Z","updated_at":"2025-06-28T18:33:17.615Z","avatar_url":"https://github.com/Aleshmandr.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mace\nMace is a MVVM framework for Unity\nThis framework is used in the [Uice framework](https://github.com/Aleshmandr/Uice) but can be used separately.\n\n## Installation\nYou can add this Git package via Unity package manager:\nhttps://github.com/Aleshmandr/Mace.git\n\n## Framework's Philosophy\nThe framework encourages a [Model-View-ViewModel](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel) approach, splitting the main concerns of any UI piece into a couple of classes that interoperate together.\n\n![MVVM diagram](https://user-images.githubusercontent.com/3226755/94297948-9f713e00-ff65-11ea-9dce-f44bbef708a6.png)\n\n* The **model** is any raw data that your game should display to the user. It could be player status, enemies status, systems information, configuration, etc.\n\n* The **view** is a passive interface that displays data (the model) and routes user commands (events) to the viewmodel to act upon that data. It is typically a GameObject built of uGui components with a main class to handle them.\n\n* The **viewmodel** is an intermediary class that retrieves data from the model and formats it for display in the view. It also reacts to user events in the view and updates the model.\n\nMace contains some base classes and systems that implement this pattern to let you focus on your game's concrete needs.\n\n## Observables\nThe observable family is the bread and butter of Mace. They act as the glue that keeps everything interconnected.\n\nThe View and the ViewModel communicate with each other through observable objects. These classes have mechanisms to automatically notify observers about changes in their internal state.\n\nThere are four members in the observable family, each of them intended for a particular need in the system. \n\n### ObservableVariable\nThis is the battle horse of the whole system; one of the simpler and most useful of all the members of the family. It just wraps a variable and notifies when its value changes. It's worth noting that it won't raise a change event when its `.Value` is set with the same value that is already stored.\n\n### ObservableCollection\nIt represents a collection or a list of elements of the same type. You'll find all operations that you'd expect for a regular `List\u003cT\u003e`, and it'll notify a particular event for each of them.\n\nThe `ObservableCollection` grants you a lot of control when dealing with collections of data and provides an extensive pool of relevant information about the events that take place for those entities listening for changes in its contents.\n\n### ObservableCommand\nThe `ObservableCommand` is the channel through which the view can communicate with the ViewModel when the user performs an action with the intention to change the underlying model.\n\nIn addition to an `.Execute()` method to request an action by the ViewModel, it also exposes an `ObservableVariable\u003cbool\u003e` that tells the requester whether said action can be performed or not in this particular moment. This is really useful to give the user feedback about the available actions they have at their disposal, by greying out buttons when `.CanExecute.Value` is `false`, for example.\n\nThere are two versions of `ObservableCommand`, one of them with a generic parameter `T`, so you can add some information about the requested action.\n\n### ObservableEvent\nThe last member of the family and the most obvious of them all. An `ObservableEvent` is just... and event; something that takes place in a particular moment and which value (if any) is not supposed to be stored to be consulted in the future. \n\nLike the `ObservableCommand`, the event has a generic version that can be used to supply additional information about the observed happening.\n\n## ViewModel\nThe `ViewModel` is some sort of _translator_ between your business model and the view. It holds all the data that the view requires in a simple and easy to consume format. \n\nFor that matter, it exposes Observable objects and keeps them up to date according to changes in the business model. Included in those observable objects, there could be `ObservableCommand`s, which are the way the View is able to communicate user driven events to the ViewModel so it can process them and update the model accordingly.\n\nThe ViewModel is **not** a `MonoBehaviour`, meaning that it can be passed away between classes in the Mace system and it doesn't necessarily live on a concrete `GameObject`. \n\nCreating a new ViewModel is pretty straightforward. You can extend the `ViewModel` class, which already handles a couple of mechanism for you and allows your code to be easily attached to the game's update loop or react to some events during the ViewModel's lifecycle. \n\n```csharp\nusing Mace;\n\npublic class MyViewModel : ViewModel\n{\n    public IReadonlyObservableVariable\u003cfloat\u003e MyVariable =\u003e myVariable;\n    public IObservableCommand MyCommand =\u003e myCommand;\n    public IObservableEvent MyEvent =\u003e myEvent;\n\n    private ObservableVariable\u003cfloat\u003e myVariable;\n    private ObservableCommand myCommand;\n    private ObservableEvent myEvent;\n\n    private MyModel model;\n\n    // You can freely use constructors\n    public MyViewModel(MyModel model)\n    {\n        myVariable = new ObservableVariable\u003cfloat\u003e(myModel.myVariable);\n\n        myCommand = new ObservableCommand();\n        myCommand.ExecuteRequested += OnMyCommandExecuteRequested;\n\n        myEvent = new ObservableEvent();\n\n        this.model = model;\n    }\n\n    // Called when the ViewModel starts being used\n    protected override void OnEnable()\n    {\n        model.NotifiedSomething += OnMyModelNotifiedSomething;\n    }\n\n    // Called when the ViewModel is put on hold and not used anymore\n    protected override void OnDisable()\n    {\n        model.NotifiedSomething -= OnMyModelNotifiedSomething;\n    }\n\n    // Update works as MonoBehaviour's Update method\n    protected override void Update()\n    {\n        myVariable.Value = model.myVariable;\n    }\n\n    private void OnMyCommandExecuteRequested()\n    {\n        model.DoSomething();\n    }\n\n    private void OnMyModelNotifiedSomething()\n    {\n        myEvent.Raise();\n    }\n}\n```\n\nYou are free to create your ViewModel from scratch instead of extending `ViewModel`. The only restriction is that you need to implement the `IViewModel` interface for it to be used by the framework.\n\nIt's worth mentioning that the ViewModel is the entry point for everything that's gonna happen in the View so it is a good idea to keep it as simple and as clean as possible. Of course, you should always keep in mind that the ViewModel is not responsible for anything related to the way the View is displaying its data. It should only provide data, not styles or behaviours. \n\n## Bindings\nBindings are the last link the framework chain. They are responsible of providing updated information about changes in the ViewModel to the Unity Components that use them.\n\n![image](https://github.com/Aleshmandr/Uice/assets/11294931/e2144732-e6aa-4be0-95b5-aa29a6f0b6e8)\n\nThere are matching binders for every member of the observable family and some other elements to ease development and displaying the information in the Editor.\n\n### Binders\n\nBinders are the actual components (`MonoBehaviour`s) that operate over a Unity `Component` so the View can reflect the internal state of the ViewModel.\n\nMace includes many binders for uGui's components as well as some other collections that let your UI objects react to changes on the ViewModel.\n\n### OperatorBinders\nThese are a special kind of binders. Binders that can process one or more values from the ViewModel and expose a derived value based on its input. \n\nThis mechanism grants Mace an important expressive power, allowing the designers and UI artists to mix and match properties to create their own to fit the requirements of the View. Remember that the View can (and will) constantly change as the project grows and evolves, and having a clear and strict separation between the ViewModel (the data to be displayed) and the View (how is that data displayed) is a guarantee for a more agile development process.\n\nOperatorBinders are heavily inspired by [ReactiveX](http://reactivex.io/documentation/operators.html) operators, so you can expect to find many of the most valuable operations from that library. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faleshmandr%2Fmace","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faleshmandr%2Fmace","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faleshmandr%2Fmace/lists"}