Giter VIP home page Giter VIP logo

unity-weld's Introduction

Unity-Weld

NuGet NuGet Build Status

MVVM-style data-binding system for Unity.

Unity-Weld is a library for Unity 5+ that enables two-way data binding between Unity UI widgets and game/business logic code. This reduces boiler-plate code that would otherwise be necessary for things like updating the UI when a property changes, removes the need for messy links between objects in the scene that can be broken easily, and allows easier unit testing of code by providing a layer of abstraction between the UI and your core logic code.

A series of articles on Unity Weld has been published on What Could Possibly Go Wrong.

Example Unity project can be found here: https://github.com/Real-Serious-Games/Unity-Weld-Examples.

Installation

To install Unity-Weld in a new or existing Unity project:

  • Load Unity-Weld.sln in Visual Studio and build it
  • Copy UnityWeld.dll into your Unity project and place in any directory within Assets
  • Copy UnityWeld_Editor.dll into your Unity project and place it inside an Editor folder within Assets

Alternatively, just copy the UnityWeld/Binding and UnityWeld/Widgets folders into your Assets directory in your Unity project, and copy all the .cs files in UnityWeld_Editor to a folder named Editor inside your Assets directory.

Getting started

Check out the Unity-Weld-Examples repository for some examples of how to use Unity-Weld.

API docmentation is on our wiki.

If you're interested in getting involved feel free to check out the roadmap on Trello, or submit a pull request. Make sure to read our contributing guide first.

unity-weld's People

Contributors

ashleydavis avatar edycer avatar kalvinpearce avatar morganmoon avatar rorydungan avatar ulysseswu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

unity-weld's Issues

InputField binding binds to wrong event

In UnityEventWatcher.GetBoundEvent you call
var boundEvent = GetBindableEvents(component).FirstOrDefault();
which always returns the first event regardless of the boundEventName passed into the function. As a result, InputFields always bind to the inputEnd event and cannot be bound to the valueChanged event. Probably should be
var boundEvent = GetBindableEvents(component).FirstOrDefault(e=>e.Name.Equals(boundEventName));

[Suggestion] use `AddComponentMenu` attributes for components

When I add components for UI objects (Text, Slider etc.), I have to find OneWayPropertyBinding through (I don't want to use keyboard🤣 ):

Add Component -> Scripts -> UnityWeld.Bindings -> One Way Property Binding

However, if you could add [AddComponentMenu("Unity Weld/OneWay Property Binding")] beyond public class OneWayPropertyBinding : AbstractMemberBinding, we can simply the process:

Add Component ->Unity Weld -> One Way Property Binding

Problem of TypeResolver for Interface

Hi,

In GetPublicMethods function, when the target is an interface, it will not return any MethodInfo.

[Binding] public interface A { [Binding] void TestFunc(); }

It is ok the change the following line from

return (new[] { type }) .Concat(type.GetInterfaces()) .SelectMany(i => i.GetMethods(BindingFlags.Public | BindingFlags.Instance));

to

return (new[] { type }) .Concat(type.GetInterfaces()) .SelectMany(i => i.GetMethods());

Or what is the correct way to do the reflection on interface method. Thanks.

Bugged images after binding a Sprite in Image Component using OneWayPropertyBinding

When using One Way Property Binding to bind a public bindable Sprite property from my ViewModel it seems Weld is overriding something more than just the sprite itself, as I it stops resizing to RectTransform properly as it should be and also the image is stretched despite being set to constrain original proportions.
I'm using Unity Beta 2019.1.0b4, which also can generate problems, but all the images without binding act properly.

(Fix Included) Does not update all bindings in VM when passing Null or String.Empty

The original INotifyPropertyChanged allows a user to pass a PropertyChangedEventArgs of null or String.Empty as a shorthand for updating all registered bindings on the current ViewModel.

After looking over your code I found re-enabling this was VERY simple.

Inside PropertyWatcher.cs at the bottom you have:

private void propertyOwner_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == propertyName) { action(); } }

By simply adding "|| String.IsNullOrEmpty(e.PropertyName)" to the if statement the expected behavior can be achieved. Since the PropertyWatcher class is already designed to be limited to the scope of the current view model, no further code is needed.

private void propertyOwner_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == propertyName || String.IsNullOrEmpty(e.PropertyName)) { action(); } }

Implement Clean Mvvm Architecture

Hi, I’m looking for Mvvm plugins for Unity and your solution is currently the best I've found.

I worked with Mvvm for over a year on Xamarin technology and clearly understand its work. But what you have is different from pure Mvvm. I look at MvvmCross and this is a great example for me of cross-platform Mvvm.

I think this framework could be partially ported to Unity. MvvmCross Unity3d.
Now I am trying to implement something similar in Unity in a new project, but I have to write it on the go. The concept of what I am writing is very similar to the MvvmCross, excluding the View part.

Below I gave an example of running my program in Unity.

My ViewModels are pure C # classes, they are inherited from the ViewModel class, which implements the underlying logiс.
My Views- MonoBehaviour, are inherited from the base View class, which implements the basic logic and events.

I try to implement ViewModel-first navigation andcreate a basic asynchronius (UniRx with UniTask) NavigationService to navigate between ViewModels. In GameSettings, I defined Views (which are represented as an AssetReference using the AddressableSystem) and the name ViewModel. For some connection.
NavigationService has a Load method that loads the entire View asynchronously and attaches a ViewModel to them. Yea, thats ViewModel-first :)
image

On Start:

  1. I use Zenject to register services and my ViewModels.
  2. Then i run LoadingViewModel. And call NavigationService.Load
  3. Navigate to some MainViewModel

View class like a MyPage.xaml.cs it needs to bind texts, buttons and etc. to ViewModel:
image

For text binding i use ReactiveProperty from UniRx.

But my bindings are frankly shit. And I want to implement Fluet binding method from MvvmCross.

What is this post / question for. I'm looking for people who want to do something like this. Ideally, I want to migrate MvvmCross to Unity. If it will be interesting to you, then I can provide a more detailed implementation of my approach, but it is not perfect and mainly depends on external plug-ins.

Thanks

FloatToStringAdapterOptions removed at runtime

I am using a text field to read read/write a float value using a TwoWayPropertyBinding.

In the project view I have created some FloatToStringAdapterOptions and assigned it in the binding editor.

However, at runtime I get these errors:

Failed to convert value from Tooloop.DisplayGridViewModel.SizeX (Single) to TMPro.TMP_InputField.text (String).
UnityEngine.Debug:LogError(Object, Object)
UnityWeld.Binding.Internal.PropertySync:SyncFromSource()
UnityWeld.Binding.TwoWayPropertyBinding:Connect()
UnityWeld.Binding.AbstractMemberBinding:Init()
UnityWeld.Binding.AbstractMemberBinding:Start()

NullReferenceException: Object reference not set to an instance of an object
UnityWeld.Binding.Adapters.FloatToStringAdapter.Convert (System.Object valueIn, UnityWeld.Binding.AdapterOptions options) (at <72527601dcb64d0392e80b9b9438fc5f>:0)
UnityWeld.Binding.Internal.PropertyEndPoint.SetValue (System.Object input) (at <72527601dcb64d0392e80b9b9438fc5f>:0)
UnityWeld.Binding.Internal.PropertySync.SyncFromSource () (at <72527601dcb64d0392e80b9b9438fc5f>:0)
UnityEngine.Debug:LogException(Exception)
UnityWeld.Binding.Internal.PropertySync:SyncFromSource()
UnityWeld.Binding.TwoWayPropertyBinding:Connect()
UnityWeld.Binding.AbstractMemberBinding:Init()
UnityWeld.Binding.AbstractMemberBinding:Start()

The latter complains about missing adapter options which indeed are removed in the binding editor.

Am I using this wrong?
How do I correctly assign the adapter options?

Access object of collection

I setup a collection binding with a collection like this:

[Binding]
public class MediaCollectionViewModel : MonoBehaviour
{
    [Binding]
    public ObservableList<MediaViewModel> Media { get; set; } = new ObservableList<MediaViewModel>()
    {
        new MediaViewModel(new Media(){
            Name = "Media1"
        }),
        new MediaViewModel(new Media(){
            Name = "Media2"
        }),
        new MediaViewModel(new Media(){
            Name = "Media3"
        }),
        new MediaViewModel(new Media(){
            Name = "Media4"
        }),
    };
}
[Binding]
public class MediaViewModel : ObservableObject
{
    [Binding]
    public Media Media
    {
        get { return media; }
        set {
            if (media == value) return;
            media = value; OnPropertyChanged();
        }
    }
    Media media;

    public MediaViewModel(Media _media)
    {
        Media = _media;
    }
}
[Binding]
public class Media
{
    [Binding]
    public string Name
    {
        get { return name; }
        set {
            if (name == value) return;
            name = value; OnPropertyChanged();
        }
    }
}

How can i access a Media object in my Collection template and display the text it contains? I cant seem to find a way :/

Binding components have issues with SubViewModel's

Binding components cannot find any SubViewModels. It seems to be an issue with the editor side of the binding components (ex. One Way Property Binding). This issue is reproducible in the example projects, if you open it up and go into the "Template_selector" example, it all seems to work fine (Which is where I think this is coming from the editor), however in the Hierarchy if you go under Canvas->Output and any of the text which is suppose to binded, the binding components seem to be missing information (even though they are working fine, I think this is because these values were set in an older version of Unity-Weld). IF you remove the One Way Binding Component and try to recreate it, you will find it to be impossible, and now the binding is broken. The error you receive is: ApplicationException: Could not find the specified view model "ViewModelClassName".

Thanks!

Some types don't bind correctly (Regression in PR #11)

PR #11 Binding to types that cast implicitly added the ability to convert without an adapter between types that can cast implicitly like float and double. This relies on the Convert.ChangeType method, which assumes the value being converted implementes the IConvertable interface.

The problem with this is that if the value doesn't implement IConvertible the method will just throw an exception and fail. This doesn't seem to happen in the case where the type already matches the conversion type, which is why it wasn't caught previously.

The problem occurs when converting a reference to a derived type to a reference of the type of the base class, such as converting from Texture2D to Texture. In this case, it should work without calling Convert.ChangeType, but because we're calling that for all values and because Texture2D doesn't implement IConvertable, it fails.

If we can't figure out a simple solution I'm thinking we should probably revert that feature since it was a convenience and the same functionality could be achieved with adapters.

Why custom ObservableList?

Hello,

Could you please explain why do you create custom 'ObservableList' and didn't use 'ObservableCollection' from System.Collections.ObjectModel?

This makes usage of UnityWeld very limited. You force users of UnityWeld to has tight coupling with it. Which provides limitations like: can't switch between mvvm frameworks, can't use different frameworks for different platforms/engines, can't have common UI code for view implementations, constantly has a ref to UnityWeld everywhere.

As far as I know .Net 3.5 is deprecated long time ago
https://forum.unity.com/threads/net-3-5-runtime-has-been-deprecated-in-unity-2018-3.601384/

and .netstandard 2.0 and 4.x api (in Unity) perfectly support ObservableCollection.

Can we just get rid of .net 3.5 already and use standard ObservableCollection?

Thanks.

Event Binding Execution Order Issue causing Race Condition

I have a slider in which I catch, BeginDrag and EndDrag events.

When I bind them in the inspector Using Unity Event Bindings, events are called in order as expected.
BeginDrag when drag started, EndDrag when mouse is up and dragging is Done.
image

However when I bind those events to Weld on the same components, thus 2 Event Binders,
BeginDrag Event bubbles up after EndDrag is invoked.
image

I wondering if anyone else seen similar issues with binding, or using 2 EventBinders is a problem on the same component.

Binding to game object visibility

I want to disable some of my UI based on the value of a bool in my view model. I was hoping I would be able to bind to gameObject.active but I guess turning off the game object would disable the component so it would never turn back on. Is there a better approach?

AOT Reflection issue when compiling against .NET 4.x runtime in Unity 2018.1

Hello! I've been using your excellent framework extensively for our UI, but I've hit a snag.

It looks like reflection methods are more restrictive under the .NET 4.x scripting backend than it was with 3.x, and I'm seeing the following when running on iOS having compiled under 4.x in Unity 2018.1.

[Crashlytics] Recording exception: ExecutionEngineException: Attempting to call method 'System.Reflection.MonoProperty::GetterAdapterFrame' for which no ahead of time (AOT) code was generated.
UnityEngine.DebugLogHandler:Internal_Log(LogType, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
Fabric.Internal.Runtime.Utils:Log(String, String)
Fabric.Internal.Crashlytics.CrashlyticsInit:HandleLog(String, String, LogType)
UnityEngine.LogCallback:Invoke(String, String, LogType)
UnityEngine.Application:CallLogCallback(String, String, LogType, Boolean)
UnityEngine.DebugLogHandler:Internal_LogException(Exception, Object)
UnityEngine.DebugLogHandler:LogException(Exception, Object)
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
UnityWeld.Binding.Internal.PropertySync:SyncFromSource()
UnityWeld.Binding.<>c__DisplayClass17_0:<Connect>b__0()
System.Action:Invoke()
UnityWeld.Binding.Internal.PropertyWatcher:propertyOwner_PropertyChanged(Object, PropertyChangedEventArgs)
System.ComponentModel.PropertyChangedEventHandler:Invoke(Object, PropertyChangedEventArgs)
MyCustomViewModel:OnPropertyChanged(String)

Dropdown Binding automatic updates upon list changes

In Unity-Weld-Examples, there is one Dropdown example. The example shows how to connect to a simple string[] array in the view model, but that only updates the list once upon initialization.

I'm having a hard time figuring out how to bind to a list so it will automatically update upon changes in that list. The example uses a simple string[] as the type, but I noticed that there are classes like ObservableList and BoundObservableList. So I thought I could maybe use those types of lists instead, but no luck. Simply making the string[] array property a [Binding] and doing a call to OnPropertyChanged("MyList") when the list is changed, seems to have no effect.

I looked at the code of DropdownBinding, but couldn't figure out if and how it could be done. I feel like I'm missing something really obvious here, so that's why I thought it's probably best to ask you.

On a side note: I'm using this to link to items from a database, so I'd like to use the ID of those items as the actual value that is used as the Selection value. I'm pretty sure that could be done by using the "Selection View Model To UI Adapter", "Selection UI To View Model Adapter" and "Options Adapter" settings. So hopefully a solution exists where I could solve both of these "problems" at the same time. Thanks.

Image Binder

Hello! Thanks for the project, is there a possibility of binding a picture or for the next it is necessary to write your adapter? Is there still an option to make a list with a pool of objects so as not to load everything into the UI and load it like that?

TwoWayBinding on a Slider with Min Value not set to 0 fails

I've created a standard Unity UI Slider and added a TwoWayBinding component.
This works fine, until you change the Min Value on the Slider to something different than 0.

What happens is, that the view-model property that is bound to this Slider is first called with the correct value (when changing the slider). But after that it's called a second time with 'value' set to the Min Value. This isn't reflected in the UI though, so things seem to be working, when in reality they aren't.

I looked at the Call Stack for both these calls and noticed something. The first (correct) call originates from a UnityEngine.UI.Slider:OnPointerDown event. The second (incorrect) call originates from a UnityEngine.UI.Slider:OnDrag event. The value always seems to be 0 in this second call. Which is is outside the range for the Slider, so it's corrected back to Min Value.

While typing this, I tried a few more things and it seems that this second call always happens. Also when Min Value is set to 0. I can see my property's "set" being called, but somehow the correct value ends up in my private field.

I think this might not have anything to do with Unity-Weld at this point, but I'm totally confused as to what's going on here. My work around for now is to set the Min Value to 0, because then everything seems to work as expected, even though I see these weird calls with value 0 happening all the time.

Ahead of time Compilation Problem with Binding on iOS

When I try to run my game on an iOS Device and use the Collection-Binding I get an error after setting the BindableCollection. XCode shows:
ExecutionEngineException: Attempting to call method 'UnityWeld.Binding.Internal.UnityEventBinder`1[[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]::.ctor' for which no ahead of time (AOT) code was generated.

I tried managed bytcode stripping with the link.xml file and content:

<linker>
    <assembly fullname="UnityWeld" preserve="all"/>
    <assembly fullname="UnityWeld.Binding" preserve="all"/>
    <assembly fullname="UnityWeld.Binding.Internal" preserve="all"/>
</linker>

but with no success.

Is there some workarounds for that?

CollectionBinding to unassigned property

Hey again,

I have a viewmodel with observable list property. This property watches Model property. initial value of both is null and assigned later. Which is perfectly normal behavior in WPF for example.

But UnityWeld does't allow null for observable list. And I wonder why?
I see in code that CollectionBinding watches this property and rebind if necessary.
And it looks like redundant limitation.
Do I miss something? Can we just silently wait for value instead of exception?

I can implement that, but I realized that I did my previous PR in master, and all my work will go to that PR :) but you can cherry-pick this change...or may be you have an better idea...

CollectionBinding.cs

line 141

            var viewModelValue = viewModelCollectionProperty.GetValue(viewModel, null);
            if (viewModelValue == null)
            {
                throw new PropertyNullException(
                    "Cannot bind to null property in view: " 
                    + ViewModelPropertyName
                );
            }

Thanks.

How to properly bind to ViewModel data that is avaialbe after Awake?

This seems like a a great library, and I might be severely missing the point, but I don't see a way out of the box to support binding to ViewModels that have their data populated after Awake (since initializiation for binders seems to happen on Awake).
e.g.

  • when the ViewModel is created at runtime, and then initialized (since Unity doesn't allow initializing a GameObject before Awake except if it's disabled, and that's an ugly flow for prefabs)
  • or even, using IViewModelProvider to have POCO VMs, as you suggested in previous issue replies: how can GetViewModel return a valid non-MonoBehaviour instance that's somehow injected into the MonoBehaviour (ony way I see is you'd have to call some static singleton-ish provider class from within the method for it to happen before Awake, and I'd like to be able to stick with a DI flow)

Adding [DefaultExecutionOrder(100)] to AbstractMemeberBinding fixes it for VMs already in the scene at lauch, but it doesn't work for those created at runtime.

Any insights welcome! Thanks!

Binding through custom string instead of function name

Is it possible somehow to bind callbacks through strings instead of function names?

Something like

[Binding("BindingName")]

so that BindingName will be actually shown in the dropbox instead of the method of the class?

This is needed to allow refactoring, so that I can change the name of the method without needing to touch the UI.

Is a similar solution already available?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.