usingUnityEngine;usingCoherence.Toolkit;[CustomBindingProvider(typeof(Health))]publicclassHealthBindingProvider:CustomBindingProvider{ // check the CustomBindingProvider base class to // explore the possibilities!}
Place CustomBindingProviders inside an editor folder.
In order for these new custom bindings to work on reflected mode, we'll need to implement a runtime serializer that understands how to deal with them.
Check the CustomBinding.Descriptor API for further properties, like interpolation or marking the binding as required.
CustomBindingRuntimeSerializer
For custom bindings to work on reflected mode, we need to implement how their values are serialized and deserialized at runtime:
usingUnityEngine;usingCoherence.Toolkit;usingCoherence.Entity;usingCoherence.SimulationFrame;// this class defines how reflected mode serializes and deserializes Health's custom bindingspublicclassHealthRuntimeSerializer:CustomBindingRuntimeSerializer{publicoverridevoidSerialize(CustomBinding customBinding,ICoherenceComponentData componentData) {var health =customBinding.componentasHealth;if (customBinding.descriptor.name=="debugBinding") {Debug.Log("[HEALTH] Serializing debugBinding"); }elseif (customBinding.descriptor.name=="myCustomBinding") { // in this example, lets send myCustomBinding as "health" over the networkvar fi =componentData.GetType().GetField("value");fi.SetValue(componentData,health.health); } } public override void Deserialize(CustomBinding customBinding, ICoherenceComponentData componentData, long clientFrame)
{var health =customBinding.componentasHealth;if (customBinding.descriptor.name == "debugBinding") {Debug.Log("[HEALTH] Deserializing debugBinding"); }elseif (customBinding.descriptor.name == "myCustomBinding") {var fi =componentData.GetType().GetField("value");var v = (float)fi.GetValue(componentData); // we take the value of myCustomBinding, which we know we serialized with the value of health // in this example, we just do some custom logic: updating the isAlive bool with ithealth.isAlive= v >0; } }publicoverridevoidInterpolate(CustomBinding customBinding,double time) { }publicoverridevoidSetToLastSample(CustomBinding customBinding) { }}
CustomBindingRuntimeSerializers should be placed in a non-editor folder.
Once we have our runtime serializer, we need to make our binding provider use it:
You can extend an already existing CustomBindingProvider. For example, coherence ships with a CustomBindingProvider for Transforms:
usingSystem.Collections.Generic;usingCoherence.Toolkit;usingUnityEngine;[CustomBindingProvider(typeof(Transform))]publicclassCustomTransformBindingProvider:CustomBindingProvider{publicoverridevoidFetchDescriptors(List<CustomBinding.Descriptor> descriptors) { base.FetchDescriptors(descriptors); // your changes }}
This way, you can define additional rules on how you want to treat your Transforms, for example.
Any amount of CustomBindingProviders can be registered over the same component, but only one being used. The one being used, is resolved by a priority integer that you can specify in the CustomBindingProvider attribute. The class with the higher priority defined in the attribute will be the only provider taken into account:
[CustomBindingProvider(typeof(Transform),1)]
The default priority is set at 0, and coherence's internal CustomBindingProviders have a priority of -1.
To understand how these APIs are used, check out TransformBindingProvider and AnimatorBindingProvider, both shipped with the coherence SDK (<package>/Coherence.Editor/Toolkit/CustomBindingProviders).