Inconsistent Public Member Access in UnityEvents

I’ve been getting frustrated setting public members via the UnityEvent interface, so I decided to do a little testing. From what I can tell, UnityEvents are pretty inconsistent with the rest of the editor in terms of field/property serialization. As noted by others, public fields are not settable from UnityEvents, requiring either a public property or method instead. In addition, I’m now finding that only public properties of certain types are settable from UnityEvents.

Consider the code and screenshot below. It looks like only my int, float, and UnityEngine.Object-derived properties can be set via UnityEvents, even though all my fields are serialized by Unity in the Inspector for the component itself. If its not already on the road map, I strongly request the ability to set public fields and properties of all .NET types (and other Unity types like Vector3) via UnityEvents. I’m sure this is way more complicated to implement than I realize, but it would really help users implement some complex logic in the Inspector, without writing code.

using System;
using UnityEngine;

namespace HighHandHoldem.Unity {
    public class Derp : MonoBehaviour {

        public byte ByteField;
        public byte ByteProp { get; set; }

        public sbyte SbyteField;
        public sbyte SbyteProp { get; set; }

        public char CharField;
        public char CharProp { get; set; }

        public short ShortField;
        public short ShortProp { get; set; }

        public ushort UshortField;
        public ushort UshortProp { get; set; }

        public int IntField;
        public int IntProp { get; set; }

        public uint UintField;
        public uint UintProp { get; set; }

        public long LongField;
        public long LongProp { get; set; }

        public ulong UlongField;
        public ulong UlongProp { get; set; }

        public float FloatField;
        public float FloatProp { get; set; }

        public double DoubleField;
        public double DoubleProp { get; set; }

        public decimal DecimalField;
        public decimal DecimalProp { get; set; }

        public Vector2 Vector2Field;
        public Vector2 Vector2Prop { get; set; }

        public Vector2Int Vector2IntField;
        public Vector2Int Vector2IntProp { get; set; }

        public Vector3 Vector3Field;
        public Vector3 Vector3Prop { get; set; }

        public Vector3Int Vector3IntField;
        public Vector3Int Vector3IntProp { get; set; }

        public enum DerpEnum { Herp, Derp }
        public DerpEnum EnumField;
        public DerpEnum EnumProp { get; set; }

        [Serializable]
        public class SerializableClass { }
        public SerializableClass ClassField;
        public SerializableClass ClassProp { get; set; }

        public MonoBehaviour MonoBehaviourField;
        public MonoBehaviour MonoBehaviourProp { get; set; }

        public class DerivedBehaviour : MonoBehaviour { }
        public DerivedBehaviour DerivedBehaviourField;
        public DerivedBehaviour DerivedBehaviourProp { get; set; }

        public ScriptableObject ScriptableObjectField;
        public ScriptableObject ScriptableObjectProp { get; set; }

        public class DerivedAsset : ScriptableObject { }
        public DerivedAsset DerivedAssetField;
        public DerivedAsset DerivedAssetProp { get; set; }

    }
}

Usually you do not set fields with events. Events are primarily for calling methods. In fact you can’t set field with event. Properties are differnt form fields. When you declare a property, two methods named get_YourPropertyName and set_YourPropertyName are added under the hood to your class. The second called setter and can be called with event and the first (getter) is not because it returns a value. While events actually may call methdos returning values, it is pretty useless because that value will be lost anyway. Unity events canv handle limited amount of arguments types, but there’s UnityEvent you can derive from and send pretty much any kind of data with your event. Hope this helps.,

@palex-nx I’m aware that you can’t set fields with events; that’s what this post is about. I’m arguing that we should be able to set fields in events, without having to add some method just for that purpose or change it to a property like in that GitHub issue that I linked. Said another way, I don’t want to add a SetX() method for every field X field in my components, nor do I want to change all of my fields to properties (and incur the extra method-call overhead), just so that I can change them in response to UnityEvents.

Well, why do you think so? Event is just a wrapper on array of delegates, and field is not delegate, but property setter is. Also setting property using PropertyInfo would be dozen times slower than method call event through delegate (wich is slowest way to call a method except for reflection).

Also you do not need SetX method for every X field. In object oriented programming we have that nice encapsulation principle thing. Say your class have some fields and they represent it’s state. A wolf is running, it have direction, speed, isjump flag, stamina, whatever. You dont set those vars from outside the class. Instead, you add methods to read and write wolf’s state and change them internally to ensure state expressed in those values remains consistent. Like a wolf can’t have zero stamina and be jumping at the same time. That’s why probably nobody actually need to change fields via events. Instead, a event call some method on wolf’s class for wolf to be able to react on event. Imagine hunter appears and send his event to wolf. Now wolf sees hunter and can react somehow. Other beasts may react differently on the hunter and may be different classes so hunter actually don’t knows what wars to set and to what classes when sending his IAMHERE event

@palex-nx You make a good point about UnityEvents just wrapping arrays of delegates. However, it seems reasonable to me that the Inspector could create a delegate to set a field. E.g., UnityEventDrawer could display all public fields of a component, in addition to public properties/methods, and then run code like this somewhere: listOfDelegates.Add(() => targetObject.targetField = inspectorValue), albeit in some messier way probably involving reflection. Forgetting performance for a second, it would be very handy to let level designers change a field in this way through UnityEvent, rather than making them wait for a programmer like me to define a method first.

Getting back to performance, you’re right that all of this stuff is called as delegates by UnityEvent. However, other parts of my code are getting/setting these fields directly. I don’t want to sacrifice performance in those parts of my code by turning the field into a property. I know method-call overhead is very minor, but the parts of my code that access this field are in the “hot path”, so the overhead adds up (as described in this fabulous Unite 2017 talk). So public fields would allow me to do both, have a member that’s quick to access in code, but still accessible in the Inspector for level designers using UnityEvents.

Lastly, if we’re getting theoretical, your encapsulation argument doesn’t hold much water. Encapsulation doesn’t mean you can’t have public members that are independently settable. If my class has several variables, each of which can be publicly set without affecting the others, and there are no side-effects to setting those fields (like raising a “changed” event or something), then I would like to be able to work with fields not properties (for the performance boost) and not sacrifice the UnityEvent workflow. I’m sure you’re judging my architecture for having publicly settable fields, but as an example, one of my fields is just the player’s score. You can set it and you can get it, there are no side effects. There is no “score changed” event that needs to be raised, rather I would just like to set the score to 0 in response to a “game restarting” UnityEvent, without defining an additional SetScore() method.

I completely agree to your point what it would be handy in some tasks to just change some value as a reaction on the event. This is how things like BOLT works, check it out may be it will better suit your needs? But the implementation you propose looks messy and unclear to me.
Also when I manage a project, if I ever see some event setting two fields for any object, i will go and add method because it actually not very easy to manage dozens of values changed by dozens of events. However, for small projects or prototyping it’s pretty okay to me. But again, visual programming engines have better imeplementations of this feature than tables of setters with delegate generation in the inspector.
I would like events to be as pure as possible and if you’d like to add some functionatlity to them, its possible to inherit UnityEvent and implement custom inspector what allows setting values using FieldInfo, for instance.
Note, however, you cant genrerate delegates in runtime on all platforms, probably this feature is PC only.

@palex-nx We did use Bolt for a few months, but found that it didn’t add much value. I was very impressed by the dedication of Bolt’s development team and the quality of their documentation/tutorials; however, we found that it just added a bunch of extra boilerplate for programmers that were already familiar with the Unity API, and for non-programmers, it was only marginally easier to learn than C#…visual programming is still programming after all. And that’s why we’re so keen on flexibility with UnityEvents. If you wanted to trigger some simple changes in response to a UnityEvent with Bolt, you first had to create a flow graph, create Graph Variables for each GameObject that you wanted to affect and drag those on in the Flow Machine inspector, then open the Flow Graph editor and add units for the UnityEvent and all those Graph Variables, and finally connect them all together in sequence. That may not be as slow and laborious as I’m making it sound, but the fact is it took at least a dozen clicks to do with Bolt what you can do in like 3 clicks with UnityEvents: one to drag on the GameObject you’re affecting, one to open the function/property dropdown, one to select a function/property. And its so nice to just look at the UnityEvent and see what’s happening, rather than context-switch to a separate Editor window as with Bolt. Not to mention, Bolt still uses reflection behind the scenes. They claim that its 6x faster than vanilla reflection, and C# code-generation is a planned feature for Bolt 2.0, but considering the sheer depth of call stacks whenever Bolt’s flow graph’s are involved, we just didn’t find it worth the extra ceremony.

All that said, maybe I should explain my use case a little better, cause I’m curious how you (or anyone else following this thread?) would handle it. We’re not really trying to set a bunch of different fields on one component, rather we’re trying to set a field or two on a bunch of components. The pattern we frequently follow is to expose significant gameplay events (e.g., gameover, or shooting a weapon) as UnityEvents, then level/UI designers are free to hook up whatever they want to those events in the Inspector. They can play a sound, a particle effect, enable another component, trigger an animation or entire Timeline… Essentially, the Inspector becomes their playground. The only other way that I can picture that working is to write a script that takes references to all the necessary AudioSources, Animators, ParticleSystems, etc., and triggers them directly, rather than invoking a UnityEvent. But in my experience, that kills level/UI designers’ ability to do “exploratory development”, and makes a programmers a bottleneck for almost every piece of functionality, no matter how trivial. Have you adopted any good alternative on your own past projects, particularly when working with a team of non-programmers?

Oh, and we’ve kind of digressed from another one of my original feature requests, that Unity should allow setting properties of the other primitive .NET types in the UnityEvent inspector. I.e., not just ints and floats, but also uints, shorts, chars, enums, etc. From what I can tell, you can’t even call a function that takes one of these types as a parameter. That’s pretty limiting. If they can serialize the fields in the Inspector, I would think that they could be serialized in the UnityEvent interface, but I would love input from an actual Unity dev on this.