123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Threading;
- using UniRx.InternalUtil;
- using UniRx.Triggers;
- #if !UniRxLibrary
- using ObservableUnity = UniRx.Observable;
- #endif
- namespace UniRx
- {
- public static partial class ObserveExtensions
- {
- /// <summary>
- /// Publish target property when value is changed. If source is destroyed/destructed, publish OnCompleted.
- /// </summary>
- /// <param name="fastDestroyCheck">If true and target is UnityObject, use destroyed check by additional component. It is faster check for lifecycle but needs initial cost.</param>
- public static IObservable<TProperty> ObserveEveryValueChanged<TSource, TProperty>(this TSource source, Func<TSource, TProperty> propertySelector, FrameCountType frameCountType = FrameCountType.Update, bool fastDestroyCheck = false)
- where TSource : class
- {
- return ObserveEveryValueChanged(source, propertySelector, frameCountType, UnityEqualityComparer.GetDefault<TProperty>(), fastDestroyCheck);
- }
- /// <summary>
- /// Publish target property when value is changed. If source is destroyed/destructed, publish OnCompleted.
- /// </summary>
- public static IObservable<TProperty> ObserveEveryValueChanged<TSource, TProperty>(this TSource source, Func<TSource, TProperty> propertySelector, FrameCountType frameCountType, IEqualityComparer<TProperty> comparer)
- where TSource : class
- {
- return ObserveEveryValueChanged(source, propertySelector, frameCountType, comparer, false);
- }
- /// <summary>
- /// Publish target property when value is changed. If source is destroyed/destructed, publish OnCompleted.
- /// </summary>
- /// <param name="fastDestroyCheck">If true and target is UnityObject, use destroyed check by additional component. It is faster check for lifecycle but needs initial cost.</param>
- public static IObservable<TProperty> ObserveEveryValueChanged<TSource, TProperty>(this TSource source, Func<TSource, TProperty> propertySelector, FrameCountType frameCountType, IEqualityComparer<TProperty> comparer, bool fastDestroyCheck)
- where TSource : class
- {
- if (source == null) return Observable.Empty<TProperty>();
- if (comparer == null) comparer = UnityEqualityComparer.GetDefault<TProperty>();
- var unityObject = source as UnityEngine.Object;
- var isUnityObject = source is UnityEngine.Object;
- if (isUnityObject && unityObject == null) return Observable.Empty<TProperty>();
- // MicroCoroutine does not publish value immediately, so publish value on subscribe.
- if (isUnityObject)
- {
- return ObservableUnity.FromMicroCoroutine<TProperty>((observer, cancellationToken) =>
- {
- if (unityObject != null)
- {
- var firstValue = default(TProperty);
- try
- {
- firstValue = propertySelector((TSource)(object)unityObject);
- }
- catch (Exception ex)
- {
- observer.OnError(ex);
- return EmptyEnumerator();
- }
- observer.OnNext(firstValue);
- return PublishUnityObjectValueChanged(unityObject, firstValue, propertySelector, comparer, observer, cancellationToken, fastDestroyCheck);
- }
- else
- {
- observer.OnCompleted();
- return EmptyEnumerator();
- }
- }, frameCountType);
- }
- else
- {
- var reference = new WeakReference(source);
- source = null;
- return ObservableUnity.FromMicroCoroutine<TProperty>((observer, cancellationToken) =>
- {
- var target = reference.Target;
- if (target != null)
- {
- var firstValue = default(TProperty);
- try
- {
- firstValue = propertySelector((TSource)target);
- }
- catch (Exception ex)
- {
- observer.OnError(ex);
- return EmptyEnumerator();
- }
- finally
- {
- target = null;
- }
- observer.OnNext(firstValue);
- return PublishPocoValueChanged(reference, firstValue, propertySelector, comparer, observer, cancellationToken);
- }
- else
- {
- observer.OnCompleted();
- return EmptyEnumerator();
- }
- }, frameCountType);
- }
- }
- static IEnumerator EmptyEnumerator()
- {
- yield break;
- }
- static IEnumerator PublishPocoValueChanged<TSource, TProperty>(WeakReference sourceReference, TProperty firstValue, Func<TSource, TProperty> propertySelector, IEqualityComparer<TProperty> comparer, IObserver<TProperty> observer, CancellationToken cancellationToken)
- {
- var currentValue = default(TProperty);
- var prevValue = firstValue;
- while (!cancellationToken.IsCancellationRequested)
- {
- var target = sourceReference.Target;
- if (target != null)
- {
- try
- {
- currentValue = propertySelector((TSource)target);
- }
- catch (Exception ex)
- {
- observer.OnError(ex);
- yield break;
- }
- finally
- {
- target = null; // remove reference(must need!)
- }
- }
- else
- {
- observer.OnCompleted();
- yield break;
- }
- if (!comparer.Equals(currentValue, prevValue))
- {
- observer.OnNext(currentValue);
- prevValue = currentValue;
- }
- yield return null;
- }
- }
- static IEnumerator PublishUnityObjectValueChanged<TSource, TProperty>(UnityEngine.Object unityObject, TProperty firstValue, Func<TSource, TProperty> propertySelector, IEqualityComparer<TProperty> comparer, IObserver<TProperty> observer, CancellationToken cancellationToken, bool fastDestroyCheck)
- {
- var currentValue = default(TProperty);
- var prevValue = firstValue;
- var source = (TSource)(object)unityObject;
- if (fastDestroyCheck)
- {
- ObservableDestroyTrigger destroyTrigger = null;
- {
- var gameObject = unityObject as UnityEngine.GameObject;
- if (gameObject == null)
- {
- var comp = unityObject as UnityEngine.Component;
- if (comp != null)
- {
- gameObject = comp.gameObject;
- }
- }
- // can't use faster path
- if (gameObject == null) goto STANDARD_LOOP;
- destroyTrigger = GetOrAddDestroyTrigger(gameObject);
- }
- // fast compare path
- while (!cancellationToken.IsCancellationRequested)
- {
- var isDestroyed = destroyTrigger.IsActivated
- ? !destroyTrigger.IsCalledOnDestroy
- : (unityObject != null);
- if (isDestroyed)
- {
- try
- {
- currentValue = propertySelector(source);
- }
- catch (Exception ex)
- {
- observer.OnError(ex);
- yield break;
- }
- }
- else
- {
- observer.OnCompleted();
- yield break;
- }
- if (!comparer.Equals(currentValue, prevValue))
- {
- observer.OnNext(currentValue);
- prevValue = currentValue;
- }
- yield return null;
- }
- yield break;
- }
- STANDARD_LOOP:
- while (!cancellationToken.IsCancellationRequested)
- {
- if (unityObject != null)
- {
- try
- {
- currentValue = propertySelector(source);
- }
- catch (Exception ex)
- {
- observer.OnError(ex);
- yield break;
- }
- }
- else
- {
- observer.OnCompleted();
- yield break;
- }
- if (!comparer.Equals(currentValue, prevValue))
- {
- observer.OnNext(currentValue);
- prevValue = currentValue;
- }
- yield return null;
- }
- }
- static ObservableDestroyTrigger GetOrAddDestroyTrigger(UnityEngine.GameObject go)
- {
- var dt = go.GetComponent<ObservableDestroyTrigger>();
- if (dt == null)
- {
- dt = go.AddComponent<ObservableDestroyTrigger>();
- }
- return dt;
- }
- }
- }
|