UniRx 에셋 추가

This commit is contained in:
Mingu Kim
2025-06-02 00:27:36 +09:00
parent 915f292d7d
commit 8a54d47b56
510 changed files with 42973 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections;
using System.Threading;
using UnityEngine;
#if !UniRxLibrary
using ObservableUnity = UniRx.Observable;
#endif
namespace UniRx
{
public static partial class AsyncOperationExtensions
{
/// <summary>
/// If you needs return value, use AsAsyncOperationObservable instead.
/// </summary>
public static IObservable<AsyncOperation> AsObservable(this AsyncOperation asyncOperation, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<AsyncOperation>((observer, cancellation) => AsObservableCore(asyncOperation, observer, progress, cancellation));
}
// T: where T : AsyncOperation is ambigious with IObservable<T>.AsObservable
public static IObservable<T> AsAsyncOperationObservable<T>(this T asyncOperation, IProgress<float> progress = null)
where T : AsyncOperation
{
return ObservableUnity.FromCoroutine<T>((observer, cancellation) => AsObservableCore(asyncOperation, observer, progress, cancellation));
}
static IEnumerator AsObservableCore<T>(T asyncOperation, IObserver<T> observer, IProgress<float> reportProgress, CancellationToken cancel)
where T : AsyncOperation
{
if (reportProgress != null)
{
while (!asyncOperation.isDone && !cancel.IsCancellationRequested)
{
try
{
reportProgress.Report(asyncOperation.progress);
}
catch (Exception ex)
{
observer.OnError(ex);
yield break;
}
yield return null;
}
}
else
{
if (!asyncOperation.isDone)
{
yield return asyncOperation;
}
}
if (cancel.IsCancellationRequested) yield break;
if (reportProgress != null)
{
try
{
reportProgress.Report(asyncOperation.progress);
}
catch (Exception ex)
{
observer.OnError(ex);
yield break;
}
}
observer.OnNext(asyncOperation);
observer.OnCompleted();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 245d77a29b1ece34e96bfc80f8c825d8
timeCreated: 1455373897
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
#if !(NETFX_CORE || NET_4_6 || NET_STANDARD_2_0 || UNITY_WSA_10_0)
using System;
namespace UniRx
{
public struct CancellationToken
{
readonly ICancelable source;
public static readonly CancellationToken Empty = new CancellationToken(null);
/// <summary>Same as Empty.</summary>
public static readonly CancellationToken None = new CancellationToken(null);
public CancellationToken(ICancelable source)
{
this.source = source;
}
public bool IsCancellationRequested
{
get
{
return (source == null) ? false : source.IsDisposed;
}
}
public void ThrowIfCancellationRequested()
{
if (IsCancellationRequested)
{
throw new OperationCanceledException();
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e02a1bf45f8861048a6014cf7eab1825
timeCreated: 1455373901
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,125 @@
#if (NET_4_6 || NET_STANDARD_2_0)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace UniRx
{
public class CoroutineAsyncBridge : INotifyCompletion
{
Action continuation;
public bool IsCompleted { get; private set; }
CoroutineAsyncBridge()
{
IsCompleted = false;
}
public static CoroutineAsyncBridge Start<T>(T awaitTarget)
{
var bridge = new CoroutineAsyncBridge();
MainThreadDispatcher.StartCoroutine(bridge.Run(awaitTarget));
return bridge;
}
IEnumerator Run<T>(T target)
{
yield return target;
IsCompleted = true;
continuation();
}
public void OnCompleted(Action continuation)
{
this.continuation = continuation;
}
public void GetResult()
{
if (!IsCompleted) throw new InvalidOperationException("coroutine not yet completed");
}
}
public class CoroutineAsyncBridge<T> : INotifyCompletion
{
readonly T result;
Action continuation;
public bool IsCompleted { get; private set; }
CoroutineAsyncBridge(T result)
{
IsCompleted = false;
this.result = result;
}
public static CoroutineAsyncBridge<T> Start(T awaitTarget)
{
var bridge = new CoroutineAsyncBridge<T>(awaitTarget);
MainThreadDispatcher.StartCoroutine(bridge.Run(awaitTarget));
return bridge;
}
IEnumerator Run(T target)
{
yield return target;
IsCompleted = true;
continuation();
}
public void OnCompleted(Action continuation)
{
this.continuation = continuation;
}
public T GetResult()
{
if (!IsCompleted) throw new InvalidOperationException("coroutine not yet completed");
return result;
}
}
public static class CoroutineAsyncExtensions
{
public static CoroutineAsyncBridge GetAwaiter(this Coroutine coroutine)
{
return CoroutineAsyncBridge.Start(coroutine);
}
#if !(CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6)))
// should use UniRx.Async in C# 7.0
#if UNITY_2018_3_OR_NEWER
#pragma warning disable CS0618
#endif
public static CoroutineAsyncBridge<WWW> GetAwaiter(this WWW www)
{
return CoroutineAsyncBridge<WWW>.Start(www);
}
#if UNITY_2018_3_OR_NEWER
#pragma warning restore CS0618
#endif
public static CoroutineAsyncBridge<AsyncOperation> GetAwaiter(this AsyncOperation asyncOperation)
{
return CoroutineAsyncBridge<AsyncOperation>.Start(asyncOperation);
}
public static CoroutineAsyncBridge GetAwaiter(this IEnumerator coroutine)
{
return CoroutineAsyncBridge.Start(coroutine);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 93ca3de3810199947871ab4a77014fa3
timeCreated: 1475193276
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 125ca82be137b8544a2b65f7150ee2d4
folderAsset: yes
timeCreated: 1455373896
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace UniRx.Diagnostics
{
public struct LogEntry
{
// requires
public string LoggerName { get; private set; }
public LogType LogType { get; private set; }
public string Message { get; private set; }
public DateTime Timestamp { get; private set; }
// options
/// <summary>[Optional]</summary>
public UnityEngine.Object Context { get; private set; }
/// <summary>[Optional]</summary>
public Exception Exception { get; private set; }
/// <summary>[Optional]</summary>
public string StackTrace { get; private set; }
/// <summary>[Optional]</summary>
public object State { get; private set; }
public LogEntry(string loggerName, LogType logType, DateTime timestamp, string message, UnityEngine.Object context = null, Exception exception = null, string stackTrace = null, object state = null)
: this()
{
this.LoggerName = loggerName;
this.LogType = logType;
this.Timestamp = timestamp;
this.Message = message;
this.Context = context;
this.Exception = exception;
this.StackTrace = stackTrace;
this.State = state;
}
public override string ToString()
{
var plusEx = (Exception != null) ? (Environment.NewLine + Exception.ToString()) : "";
return "[" + Timestamp.ToString() + "]"
+ "[" + LoggerName + "]"
+ "[" + LogType.ToString() + "]"
+ Message
+ plusEx;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 53917e87e91c0e4449402e5d85a04765
timeCreated: 1455373899
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace UniRx.Diagnostics
{
public static partial class LogEntryExtensions
{
public static IDisposable LogToUnityDebug(this IObservable<LogEntry> source)
{
return source.Subscribe(new UnityDebugSink());
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8706ef5a13e53ec46b4848a7eec5e826
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace UniRx.Diagnostics
{
public partial class Logger
{
static bool isInitialized = false;
static bool isDebugBuild = false;
public string Name { get; private set; }
protected readonly Action<LogEntry> logPublisher;
public Logger(string loggerName)
{
this.Name = loggerName;
this.logPublisher = ObservableLogger.RegisterLogger(this);
}
/// <summary>Output LogType.Log but only enables isDebugBuild</summary>
public virtual void Debug(object message, UnityEngine.Object context = null)
{
if (!isInitialized)
{
isInitialized = true;
isDebugBuild = UnityEngine.Debug.isDebugBuild;
}
if (isDebugBuild)
{
logPublisher(new LogEntry(
message: (message != null) ? message.ToString() : "",
logType: LogType.Log,
timestamp: DateTime.Now,
loggerName: Name,
context: context));
}
}
/// <summary>Output LogType.Log but only enables isDebugBuild</summary>
public virtual void DebugFormat(string format, params object[] args)
{
if (!isInitialized)
{
isInitialized = true;
isDebugBuild = UnityEngine.Debug.isDebugBuild;
}
if (isDebugBuild)
{
logPublisher(new LogEntry(
message: (format != null) ? string.Format(format, args) : "",
logType: LogType.Log,
timestamp: DateTime.Now,
loggerName: Name,
context: null));
}
}
public virtual void Log(object message, UnityEngine.Object context = null)
{
logPublisher(new LogEntry(
message: (message != null) ? message.ToString() : "",
logType: LogType.Log,
timestamp: DateTime.Now,
loggerName: Name,
context: context));
}
public virtual void LogFormat(string format, params object[] args)
{
logPublisher(new LogEntry(
message: (format != null) ? string.Format(format, args) : "",
logType: LogType.Log,
timestamp: DateTime.Now,
loggerName: Name,
context: null));
}
public virtual void Warning(object message, UnityEngine.Object context = null)
{
logPublisher(new LogEntry(
message: (message != null) ? message.ToString() : "",
logType: LogType.Warning,
timestamp: DateTime.Now,
loggerName: Name,
context: context));
}
public virtual void WarningFormat(string format, params object[] args)
{
logPublisher(new LogEntry(
message: (format != null) ? string.Format(format, args) : "",
logType: LogType.Warning,
timestamp: DateTime.Now,
loggerName: Name,
context: null));
}
public virtual void Error(object message, UnityEngine.Object context = null)
{
logPublisher(new LogEntry(
message: (message != null) ? message.ToString() : "",
logType: LogType.Error,
timestamp: DateTime.Now,
loggerName: Name,
context: context));
}
public virtual void ErrorFormat(string format, params object[] args)
{
logPublisher(new LogEntry(
message: (format != null) ? string.Format(format, args) : "",
logType: LogType.Error,
timestamp: DateTime.Now,
loggerName: Name,
context: null));
}
public virtual void Exception(Exception exception, UnityEngine.Object context = null)
{
logPublisher(new LogEntry(
message: (exception != null) ? exception.ToString() : "",
exception: exception,
logType: LogType.Exception,
timestamp: DateTime.Now,
loggerName: Name,
context: context));
}
/// <summary>Publish raw LogEntry.</summary>
public virtual void Raw(LogEntry logEntry)
{
logPublisher(logEntry);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f0ecf366503cb0644bdd90934d24da62
timeCreated: 1455373902
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using System;
namespace UniRx.Diagnostics
{
public static class ObservableDebugExtensions
{
/// <summary>
/// Debug helper of observbale stream. Works for only DEBUG symbol.
/// </summary>
public static IObservable<T> Debug<T>(this IObservable<T> source, string label = null)
{
#if DEBUG
var l = (label == null) ? "" : "[" + label + "]";
return source.Materialize()
.Do(x => UnityEngine.Debug.Log(l + x.ToString()))
.Dematerialize()
.DoOnCancel(() => UnityEngine.Debug.Log(l + "OnCancel"))
.DoOnSubscribe(() => UnityEngine.Debug.Log(l + "OnSubscribe"));
#else
return source;
#endif
}
/// <summary>
/// Debug helper of observbale stream. Works for only DEBUG symbol.
/// </summary>
public static IObservable<T> Debug<T>(this IObservable<T> source, UniRx.Diagnostics.Logger logger)
{
#if DEBUG
return source.Materialize()
.Do(x => logger.Debug(x.ToString()))
.Dematerialize()
.DoOnCancel(() => logger.Debug("OnCancel"))
.DoOnSubscribe(() => logger.Debug("OnSubscribe"));
#else
return source;
#endif
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b43f948e095c3e749a0506709be90d68
timeCreated: 1468662620
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace UniRx.Diagnostics
{
public class ObservableLogger : IObservable<LogEntry>
{
static readonly Subject<LogEntry> logPublisher = new Subject<LogEntry>();
public static readonly ObservableLogger Listener = new ObservableLogger();
private ObservableLogger()
{
}
public static Action<LogEntry> RegisterLogger(Logger logger)
{
if (logger.Name == null) throw new ArgumentNullException("logger.Name is null");
return logPublisher.OnNext;
}
public IDisposable Subscribe(IObserver<LogEntry> observer)
{
return logPublisher.Subscribe(observer);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 063f79dc45f902c459f0955d27b445d7
timeCreated: 1455373897
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace UniRx.Diagnostics
{
public class UnityDebugSink : IObserver<LogEntry>
{
public void OnCompleted()
{
// do nothing
}
public void OnError(Exception error)
{
// do nothing
}
public void OnNext(LogEntry value)
{
// avoid multithread exception.
// (value.Context == null) can only be called from the main thread.
var ctx = (System.Object)value.Context;
switch (value.LogType)
{
case LogType.Error:
if (ctx == null)
{
Debug.LogError(value.Message);
}
else
{
Debug.LogError(value.Message, value.Context);
}
break;
case LogType.Exception:
if (ctx == null)
{
Debug.LogException(value.Exception);
}
else
{
Debug.LogException(value.Exception, value.Context);
}
break;
case LogType.Log:
if (ctx == null)
{
Debug.Log(value.Message);
}
else
{
Debug.Log(value.Message, value.Context);
}
break;
case LogType.Warning:
if (ctx == null)
{
Debug.LogWarning(value.Message);
}
else
{
Debug.LogWarning(value.Message, value.Context);
}
break;
default:
break;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 882166c30c3bff841b1e12d62c392e02
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Globalization;
namespace UniRx
{
/// <summary>
/// Represents a value associated with time interval information.
/// The time interval can represent the time it took to produce the value, the interval relative to a previous value, the value's delivery time relative to a base, etc.
/// </summary>
/// <typeparam name="T">The type of the value being annotated with time interval information.</typeparam>
[Serializable]
public struct FrameInterval<T> : IEquatable<FrameInterval<T>>
{
private readonly int _interval;
private readonly T _value;
/// <summary>
/// Constructs a time interval value.
/// </summary>
/// <param name="value">The value to be annotated with a time interval.</param>
/// <param name="interval">Time interval associated with the value.</param>
public FrameInterval(T value, int interval)
{
_interval = interval;
_value = value;
}
/// <summary>
/// Gets the value.
/// </summary>
public T Value
{
get { return _value; }
}
/// <summary>
/// Gets the interval.
/// </summary>
public int Interval
{
get { return _interval; }
}
/// <summary>
/// Determines whether the current FrameInterval&lt;T&gt; value has the same Value and Interval as a specified FrameInterval&lt;T&gt; value.
/// </summary>
/// <param name="other">An object to compare to the current FrameInterval&lt;T&gt; value.</param>
/// <returns>true if both FrameInterval&lt;T&gt; values have the same Value and Interval; otherwise, false.</returns>
public bool Equals(FrameInterval<T> other)
{
return other.Interval.Equals(Interval) && EqualityComparer<T>.Default.Equals(Value, other.Value);
}
/// <summary>
/// Determines whether the two specified FrameInterval&lt;T&gt; values have the same Value and Interval.
/// </summary>
/// <param name="first">The first FrameInterval&lt;T&gt; value to compare.</param>
/// <param name="second">The second FrameInterval&lt;T&gt; value to compare.</param>
/// <returns>true if the first FrameInterval&lt;T&gt; value has the same Value and Interval as the second FrameInterval&lt;T&gt; value; otherwise, false.</returns>
public static bool operator ==(FrameInterval<T> first, FrameInterval<T> second)
{
return first.Equals(second);
}
/// <summary>
/// Determines whether the two specified FrameInterval&lt;T&gt; values don't have the same Value and Interval.
/// </summary>
/// <param name="first">The first FrameInterval&lt;T&gt; value to compare.</param>
/// <param name="second">The second FrameInterval&lt;T&gt; value to compare.</param>
/// <returns>true if the first FrameInterval&lt;T&gt; value has a different Value or Interval as the second FrameInterval&lt;T&gt; value; otherwise, false.</returns>
public static bool operator !=(FrameInterval<T> first, FrameInterval<T> second)
{
return !first.Equals(second);
}
/// <summary>
/// Determines whether the specified System.Object is equal to the current FrameInterval&lt;T&gt;.
/// </summary>
/// <param name="obj">The System.Object to compare with the current FrameInterval&lt;T&gt;.</param>
/// <returns>true if the specified System.Object is equal to the current FrameInterval&lt;T&gt;; otherwise, false.</returns>
public override bool Equals(object obj)
{
if (!(obj is FrameInterval<T>))
return false;
var other = (FrameInterval<T>)obj;
return this.Equals(other);
}
/// <summary>
/// Returns the hash code for the current FrameInterval&lt;T&gt; value.
/// </summary>
/// <returns>A hash code for the current FrameInterval&lt;T&gt; value.</returns>
public override int GetHashCode()
{
var valueHashCode = Value == null ? 1963 : Value.GetHashCode();
return Interval.GetHashCode() ^ valueHashCode;
}
/// <summary>
/// Returns a string representation of the current FrameInterval&lt;T&gt; value.
/// </summary>
/// <returns>String representation of the current FrameInterval&lt;T&gt; value.</returns>
public override string ToString()
{
return String.Format(CultureInfo.CurrentCulture, "{0}@{1}", Value, Interval);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 266d1e44d71e7774c9abc5b23773e3f1
timeCreated: 1467771656
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,325 @@
using System;
using System.Collections.Generic;
using UniRx.InternalUtil;
using UnityEngine;
namespace UniRx
{
/// <summary>
/// Inspectable ReactiveProperty.
/// </summary>
[Serializable]
public class IntReactiveProperty : ReactiveProperty<int>
{
public IntReactiveProperty()
: base()
{
}
public IntReactiveProperty(int initialValue)
: base(initialValue)
{
}
}
/// <summary>
/// Inspectable ReactiveProperty.
/// </summary>
[Serializable]
public class LongReactiveProperty : ReactiveProperty<long>
{
public LongReactiveProperty()
: base()
{
}
public LongReactiveProperty(long initialValue)
: base(initialValue)
{
}
}
/// <summary>
/// Inspectable ReactiveProperty.
/// </summary>
[Serializable]
public class ByteReactiveProperty : ReactiveProperty<byte>
{
public ByteReactiveProperty()
: base()
{
}
public ByteReactiveProperty(byte initialValue)
: base(initialValue)
{
}
}
/// <summary>
/// Inspectable ReactiveProperty.
/// </summary>
[Serializable]
public class FloatReactiveProperty : ReactiveProperty<float>
{
public FloatReactiveProperty()
: base()
{
}
public FloatReactiveProperty(float initialValue)
: base(initialValue)
{
}
}
/// <summary>
/// Inspectable ReactiveProperty.
/// </summary>
[Serializable]
public class DoubleReactiveProperty : ReactiveProperty<double>
{
public DoubleReactiveProperty()
: base()
{
}
public DoubleReactiveProperty(double initialValue)
: base(initialValue)
{
}
}
/// <summary>
/// Inspectable ReactiveProperty.
/// </summary>
[Serializable]
public class StringReactiveProperty : ReactiveProperty<string>
{
public StringReactiveProperty()
: base()
{
}
public StringReactiveProperty(string initialValue)
: base(initialValue)
{
}
}
/// <summary>
/// Inspectable ReactiveProperty.
/// </summary>
[Serializable]
public class BoolReactiveProperty : ReactiveProperty<bool>
{
public BoolReactiveProperty()
: base()
{
}
public BoolReactiveProperty(bool initialValue)
: base(initialValue)
{
}
}
/// <summary>Inspectable ReactiveProperty.</summary>
[Serializable]
public class Vector2ReactiveProperty : ReactiveProperty<Vector2>
{
public Vector2ReactiveProperty()
{
}
public Vector2ReactiveProperty(Vector2 initialValue)
: base(initialValue)
{
}
protected override IEqualityComparer<Vector2> EqualityComparer
{
get
{
return UnityEqualityComparer.Vector2;
}
}
}
/// <summary>Inspectable ReactiveProperty.</summary>
[Serializable]
public class Vector3ReactiveProperty : ReactiveProperty<Vector3>
{
public Vector3ReactiveProperty()
{
}
public Vector3ReactiveProperty(Vector3 initialValue)
: base(initialValue)
{
}
protected override IEqualityComparer<Vector3> EqualityComparer
{
get
{
return UnityEqualityComparer.Vector3;
}
}
}
/// <summary>Inspectable ReactiveProperty.</summary>
[Serializable]
public class Vector4ReactiveProperty : ReactiveProperty<Vector4>
{
public Vector4ReactiveProperty()
{
}
public Vector4ReactiveProperty(Vector4 initialValue)
: base(initialValue)
{
}
protected override IEqualityComparer<Vector4> EqualityComparer
{
get
{
return UnityEqualityComparer.Vector4;
}
}
}
/// <summary>Inspectable ReactiveProperty.</summary>
[Serializable]
public class ColorReactiveProperty : ReactiveProperty<Color>
{
public ColorReactiveProperty()
{
}
public ColorReactiveProperty(Color initialValue)
: base(initialValue)
{
}
protected override IEqualityComparer<Color> EqualityComparer
{
get
{
return UnityEqualityComparer.Color;
}
}
}
/// <summary>Inspectable ReactiveProperty.</summary>
[Serializable]
public class RectReactiveProperty : ReactiveProperty<Rect>
{
public RectReactiveProperty()
{
}
public RectReactiveProperty(Rect initialValue)
: base(initialValue)
{
}
protected override IEqualityComparer<Rect> EqualityComparer
{
get
{
return UnityEqualityComparer.Rect;
}
}
}
/// <summary>Inspectable ReactiveProperty.</summary>
[Serializable]
public class AnimationCurveReactiveProperty : ReactiveProperty<AnimationCurve>
{
public AnimationCurveReactiveProperty()
{
}
public AnimationCurveReactiveProperty(AnimationCurve initialValue)
: base(initialValue)
{
}
}
/// <summary>Inspectable ReactiveProperty.</summary>
[Serializable]
public class BoundsReactiveProperty : ReactiveProperty<Bounds>
{
public BoundsReactiveProperty()
{
}
public BoundsReactiveProperty(Bounds initialValue)
: base(initialValue)
{
}
protected override IEqualityComparer<Bounds> EqualityComparer
{
get
{
return UnityEqualityComparer.Bounds;
}
}
}
/// <summary>Inspectable ReactiveProperty.</summary>
[Serializable]
public class QuaternionReactiveProperty : ReactiveProperty<Quaternion>
{
public QuaternionReactiveProperty()
{
}
public QuaternionReactiveProperty(Quaternion initialValue)
: base(initialValue)
{
}
protected override IEqualityComparer<Quaternion> EqualityComparer
{
get
{
return UnityEqualityComparer.Quaternion;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 13c690f353ea23141aca4090d28aaa9c
timeCreated: 1455373897
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,306 @@
using System;
using System.Reflection;
using UnityEngine;
using System.Text.RegularExpressions;
using System.Collections;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UniRx
{
[System.AttributeUsage(System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public class InspectorDisplayAttribute : PropertyAttribute
{
public string FieldName { get; private set; }
public bool NotifyPropertyChanged { get; private set; }
public InspectorDisplayAttribute(string fieldName = "value", bool notifyPropertyChanged = true)
{
FieldName = fieldName;
NotifyPropertyChanged = notifyPropertyChanged;
}
}
/// <summary>
/// Enables multiline input field for StringReactiveProperty. Default line is 3.
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public class MultilineReactivePropertyAttribute : PropertyAttribute
{
public int Lines { get; private set; }
public MultilineReactivePropertyAttribute()
{
Lines = 3;
}
public MultilineReactivePropertyAttribute(int lines)
{
this.Lines = lines;
}
}
/// <summary>
/// Enables range input field for Int/FloatReactiveProperty.
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public class RangeReactivePropertyAttribute : PropertyAttribute
{
public float Min { get; private set; }
public float Max { get; private set; }
public RangeReactivePropertyAttribute(float min, float max)
{
this.Min = min;
this.Max = max;
}
}
#if UNITY_EDITOR
// InspectorDisplay and for Specialized ReactiveProperty
// If you want to customize other specialized ReactiveProperty
// [UnityEditor.CustomPropertyDrawer(typeof(YourSpecializedReactiveProperty))]
// public class ExtendInspectorDisplayDrawer : InspectorDisplayDrawer { }
[UnityEditor.CustomPropertyDrawer(typeof(InspectorDisplayAttribute))]
[UnityEditor.CustomPropertyDrawer(typeof(IntReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(LongReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(ByteReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(FloatReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(DoubleReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(StringReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(BoolReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(Vector2ReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(Vector3ReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(Vector4ReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(ColorReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(RectReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(AnimationCurveReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(BoundsReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(QuaternionReactiveProperty))]
public class InspectorDisplayDrawer : UnityEditor.PropertyDrawer
{
public override void OnGUI(Rect position, UnityEditor.SerializedProperty property, GUIContent label)
{
string fieldName;
bool notifyPropertyChanged;
{
var attr = this.attribute as InspectorDisplayAttribute;
fieldName = (attr == null) ? "value" : attr.FieldName;
notifyPropertyChanged = (attr == null) ? true : attr.NotifyPropertyChanged;
}
if (notifyPropertyChanged)
{
EditorGUI.BeginChangeCheck();
}
var targetSerializedProperty = property.FindPropertyRelative(fieldName);
if (targetSerializedProperty == null)
{
UnityEditor.EditorGUI.LabelField(position, label, new GUIContent() { text = "InspectorDisplay can't find target:" + fieldName });
if (notifyPropertyChanged)
{
EditorGUI.EndChangeCheck();
}
return;
}
else
{
EmitPropertyField(position, targetSerializedProperty, label);
}
if (notifyPropertyChanged)
{
if (EditorGUI.EndChangeCheck())
{
property.serializedObject.ApplyModifiedProperties(); // deserialize to field
var paths = property.propertyPath.Split('.'); // X.Y.Z...
var attachedComponent = property.serializedObject.targetObject;
var targetProp = (paths.Length == 1)
? fieldInfo.GetValue(attachedComponent)
: GetValueRecursive(attachedComponent, 0, paths);
if (targetProp == null) return;
var propInfo = targetProp.GetType().GetProperty(fieldName, BindingFlags.IgnoreCase | BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var modifiedValue = propInfo.GetValue(targetProp, null); // retrieve new value
var methodInfo = targetProp.GetType().GetMethod("SetValueAndForceNotify", BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (methodInfo != null)
{
methodInfo.Invoke(targetProp, new object[] { modifiedValue });
}
}
else
{
property.serializedObject.ApplyModifiedProperties();
}
}
}
object GetValueRecursive(object obj, int index, string[] paths)
{
var path = paths[index];
FieldInfo fldInfo = null;
var type = obj.GetType();
while (fldInfo == null)
{
// attempt to get information about the field
fldInfo = type.GetField(path, BindingFlags.IgnoreCase | BindingFlags.GetField | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (fldInfo != null ||
type.BaseType == null ||
type.BaseType.IsSubclassOf(typeof(ReactiveProperty<>))) break;
// if the field information is missing, it may be in the base class
type = type.BaseType;
}
// If array, path = Array.data[index]
if (fldInfo == null && path == "Array")
{
try
{
path = paths[++index];
var m = Regex.Match(path, @"(.+)\[([0-9]+)*\]");
var arrayIndex = int.Parse(m.Groups[2].Value);
var arrayValue = (obj as System.Collections.IList)[arrayIndex];
if (index < paths.Length - 1)
{
return GetValueRecursive(arrayValue, ++index, paths);
}
else
{
return arrayValue;
}
}
catch
{
Debug.Log("InspectorDisplayDrawer Exception, objType:" + obj.GetType().Name + " path:" + string.Join(", ", paths));
throw;
}
}
else if (fldInfo == null)
{
throw new Exception("Can't decode path, please report to UniRx's GitHub issues:" + string.Join(", ", paths));
}
var v = fldInfo.GetValue(obj);
if (index < paths.Length - 1)
{
return GetValueRecursive(v, ++index, paths);
}
return v;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var attr = this.attribute as InspectorDisplayAttribute;
var fieldName = (attr == null) ? "value" : attr.FieldName;
var height = base.GetPropertyHeight(property, label);
var valueProperty = property.FindPropertyRelative(fieldName);
if (valueProperty == null)
{
return height;
}
if (valueProperty.propertyType == SerializedPropertyType.Rect)
{
return height * 2;
}
if (valueProperty.propertyType == SerializedPropertyType.Bounds)
{
return height * 3;
}
if (valueProperty.propertyType == SerializedPropertyType.String)
{
var multilineAttr = GetMultilineAttribute();
if (multilineAttr != null)
{
return ((!EditorGUIUtility.wideMode) ? 16f : 0f) + 16f + (float)((multilineAttr.Lines - 1) * 13);
};
}
if (valueProperty.isExpanded)
{
var count = 0;
var e = valueProperty.GetEnumerator();
while (e.MoveNext()) count++;
return ((height + 4) * count) + 6; // (Line = 20 + Padding) ?
}
return height;
}
protected virtual void EmitPropertyField(Rect position, UnityEditor.SerializedProperty targetSerializedProperty, GUIContent label)
{
var multiline = GetMultilineAttribute();
if (multiline == null)
{
var range = GetRangeAttribute();
if (range == null)
{
UnityEditor.EditorGUI.PropertyField(position, targetSerializedProperty, label, includeChildren: true);
}
else
{
if (targetSerializedProperty.propertyType == SerializedPropertyType.Float)
{
EditorGUI.Slider(position, targetSerializedProperty, range.Min, range.Max, label);
}
else if (targetSerializedProperty.propertyType == SerializedPropertyType.Integer)
{
EditorGUI.IntSlider(position, targetSerializedProperty, (int)range.Min, (int)range.Max, label);
}
else
{
EditorGUI.LabelField(position, label.text, "Use Range with float or int.");
}
}
}
else
{
var property = targetSerializedProperty;
label = EditorGUI.BeginProperty(position, label, property);
var method = typeof(EditorGUI).GetMethod("MultiFieldPrefixLabel", BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.NonPublic);
position = (Rect)method.Invoke(null, new object[] { position, 0, label, 1 });
EditorGUI.BeginChangeCheck();
int indentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
var stringValue = EditorGUI.TextArea(position, property.stringValue);
EditorGUI.indentLevel = indentLevel;
if (EditorGUI.EndChangeCheck())
{
property.stringValue = stringValue;
}
EditorGUI.EndProperty();
}
}
MultilineReactivePropertyAttribute GetMultilineAttribute()
{
var fi = this.fieldInfo;
if (fi == null) return null;
return fi.GetCustomAttributes(false).OfType<MultilineReactivePropertyAttribute>().FirstOrDefault();
}
RangeReactivePropertyAttribute GetRangeAttribute()
{
var fi = this.fieldInfo;
if (fi == null) return null;
return fi.GetCustomAttributes(false).OfType<RangeReactivePropertyAttribute>().FirstOrDefault();
}
}
#endif
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6180f9fd2198dee44ae7f4a617529ffa
timeCreated: 1455373899
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UniRx.Triggers;
using UnityEngine;
namespace UniRx
{
public static partial class DisposableExtensions
{
/// <summary>Dispose self on target gameObject has been destroyed. Return value is self disposable.</summary>
public static T AddTo<T>(this T disposable, GameObject gameObject)
where T : IDisposable
{
if (gameObject == null)
{
disposable.Dispose();
return disposable;
}
var trigger = gameObject.GetComponent<ObservableDestroyTrigger>();
if (trigger == null)
{
trigger = gameObject.AddComponent<ObservableDestroyTrigger>();
}
#pragma warning disable 618
// If gameObject is deactive, does not raise OnDestroy, watch and invoke trigger.
if (!trigger.IsActivated && !trigger.IsMonitoredActivate && !trigger.gameObject.activeInHierarchy)
{
trigger.IsMonitoredActivate = true;
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(MonitorTriggerHealth(trigger, gameObject));
}
#pragma warning restore 618
trigger.AddDisposableOnDestroy(disposable);
return disposable;
}
static IEnumerator MonitorTriggerHealth(ObservableDestroyTrigger trigger, GameObject targetGameObject)
{
while (true)
{
yield return null;
if (trigger.IsActivated) yield break;
if (targetGameObject == null) // isDestroy
{
trigger.ForceRaiseOnDestroy(); // Force publish OnDestroy
yield break;
}
}
}
/// <summary>Dispose self on target gameObject has been destroyed. Return value is self disposable.</summary>
public static T AddTo<T>(this T disposable, Component gameObjectComponent)
where T : IDisposable
{
if (gameObjectComponent == null)
{
disposable.Dispose();
return disposable;
}
return AddTo(disposable, gameObjectComponent.gameObject);
}
/// <summary>
/// <para>Add disposable(self) to CompositeDisposable(or other ICollection) and Dispose self on target gameObject has been destroyed.</para>
/// <para>Return value is self disposable.</para>
/// </summary>
public static T AddTo<T>(this T disposable, ICollection<IDisposable> container, GameObject gameObject)
where T : IDisposable
{
return disposable.AddTo(container).AddTo(gameObject);
}
/// <summary>
/// <para>Add disposable(self) to CompositeDisposable(or other ICollection) and Dispose self on target gameObject has been destroyed.</para>
/// <para>Return value is self disposable.</para>
/// </summary>
public static T AddTo<T>(this T disposable, ICollection<IDisposable> container, Component gameObjectComponent)
where T : IDisposable
{
return disposable.AddTo(container).AddTo(gameObjectComponent);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a7474e4acdc541340a1f566b2df46355
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,683 @@
#if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2)
#define SupportCustomYieldInstruction
#endif
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using UniRx.InternalUtil;
using UnityEngine;
namespace UniRx
{
public sealed class MainThreadDispatcher : MonoBehaviour
{
public enum CullingMode
{
/// <summary>
/// Won't remove any MainThreadDispatchers.
/// </summary>
Disabled,
/// <summary>
/// Checks if there is an existing MainThreadDispatcher on Awake(). If so, the new dispatcher removes itself.
/// </summary>
Self,
/// <summary>
/// Search for excess MainThreadDispatchers and removes them all on Awake().
/// </summary>
All
}
public static CullingMode cullingMode = CullingMode.Self;
#if UNITY_EDITOR
// In UnityEditor's EditorMode can't instantiate and work MonoBehaviour.Update.
// EditorThreadDispatcher use EditorApplication.update instead of MonoBehaviour.Update.
class EditorThreadDispatcher
{
static object gate = new object();
static EditorThreadDispatcher instance;
public static EditorThreadDispatcher Instance
{
get
{
// Activate EditorThreadDispatcher is dangerous, completely Lazy.
lock (gate)
{
if (instance == null)
{
instance = new EditorThreadDispatcher();
}
return instance;
}
}
}
ThreadSafeQueueWorker editorQueueWorker = new ThreadSafeQueueWorker();
EditorThreadDispatcher()
{
UnityEditor.EditorApplication.update += Update;
}
public void Enqueue(Action<object> action, object state)
{
editorQueueWorker.Enqueue(action, state);
}
public void UnsafeInvoke(Action action)
{
try
{
action();
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
public void UnsafeInvoke<T>(Action<T> action, T state)
{
try
{
action(state);
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
public void PseudoStartCoroutine(IEnumerator routine)
{
editorQueueWorker.Enqueue(_ => ConsumeEnumerator(routine), null);
}
void Update()
{
editorQueueWorker.ExecuteAll(x => Debug.LogException(x));
}
void ConsumeEnumerator(IEnumerator routine)
{
if (routine.MoveNext())
{
var current = routine.Current;
if (current == null)
{
goto ENQUEUE;
}
var type = current.GetType();
#if UNITY_2018_3_OR_NEWER
#pragma warning disable CS0618
#endif
if (type == typeof(WWW))
{
var www = (WWW)current;
editorQueueWorker.Enqueue(_ => ConsumeEnumerator(UnwrapWaitWWW(www, routine)), null);
return;
}
#if UNITY_2018_3_OR_NEWER
#pragma warning restore CS0618
#endif
else if (type == typeof(AsyncOperation))
{
var asyncOperation = (AsyncOperation)current;
editorQueueWorker.Enqueue(_ => ConsumeEnumerator(UnwrapWaitAsyncOperation(asyncOperation, routine)), null);
return;
}
else if (type == typeof(WaitForSeconds))
{
var waitForSeconds = (WaitForSeconds)current;
var accessor = typeof(WaitForSeconds).GetField("m_Seconds", BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic);
var second = (float)accessor.GetValue(waitForSeconds);
editorQueueWorker.Enqueue(_ => ConsumeEnumerator(UnwrapWaitForSeconds(second, routine)), null);
return;
}
else if (type == typeof(Coroutine))
{
Debug.Log("Can't wait coroutine on UnityEditor");
goto ENQUEUE;
}
#if SupportCustomYieldInstruction
else if (current is IEnumerator)
{
var enumerator = (IEnumerator)current;
editorQueueWorker.Enqueue(_ => ConsumeEnumerator(UnwrapEnumerator(enumerator, routine)), null);
return;
}
#endif
ENQUEUE:
editorQueueWorker.Enqueue(_ => ConsumeEnumerator(routine), null); // next update
}
}
#if UNITY_2018_3_OR_NEWER
#pragma warning disable CS0618
#endif
IEnumerator UnwrapWaitWWW(WWW www, IEnumerator continuation)
{
while (!www.isDone)
{
yield return null;
}
ConsumeEnumerator(continuation);
}
#if UNITY_2018_3_OR_NEWER
#pragma warning restore CS0618
#endif
IEnumerator UnwrapWaitAsyncOperation(AsyncOperation asyncOperation, IEnumerator continuation)
{
while (!asyncOperation.isDone)
{
yield return null;
}
ConsumeEnumerator(continuation);
}
IEnumerator UnwrapWaitForSeconds(float second, IEnumerator continuation)
{
var startTime = DateTimeOffset.UtcNow;
while (true)
{
yield return null;
var elapsed = (DateTimeOffset.UtcNow - startTime).TotalSeconds;
if (elapsed >= second)
{
break;
}
};
ConsumeEnumerator(continuation);
}
IEnumerator UnwrapEnumerator(IEnumerator enumerator, IEnumerator continuation)
{
while (enumerator.MoveNext())
{
yield return null;
}
ConsumeEnumerator(continuation);
}
}
#endif
/// <summary>Dispatch Asyncrhonous action.</summary>
public static void Post(Action<object> action, object state)
{
#if UNITY_EDITOR
if (!ScenePlaybackDetector.IsPlaying) { EditorThreadDispatcher.Instance.Enqueue(action, state); return; }
#endif
var dispatcher = Instance;
if (!isQuitting && !object.ReferenceEquals(dispatcher, null))
{
dispatcher.queueWorker.Enqueue(action, state);
}
}
/// <summary>Dispatch Synchronous action if possible.</summary>
public static void Send(Action<object> action, object state)
{
#if UNITY_EDITOR
if (!ScenePlaybackDetector.IsPlaying) { EditorThreadDispatcher.Instance.Enqueue(action, state); return; }
#endif
if (mainThreadToken != null)
{
try
{
action(state);
}
catch (Exception ex)
{
var dispatcher = MainThreadDispatcher.Instance;
if (dispatcher != null)
{
dispatcher.unhandledExceptionCallback(ex);
}
}
}
else
{
Post(action, state);
}
}
/// <summary>Run Synchronous action.</summary>
public static void UnsafeSend(Action action)
{
#if UNITY_EDITOR
if (!ScenePlaybackDetector.IsPlaying) { EditorThreadDispatcher.Instance.UnsafeInvoke(action); return; }
#endif
try
{
action();
}
catch (Exception ex)
{
var dispatcher = MainThreadDispatcher.Instance;
if (dispatcher != null)
{
dispatcher.unhandledExceptionCallback(ex);
}
}
}
/// <summary>Run Synchronous action.</summary>
public static void UnsafeSend<T>(Action<T> action, T state)
{
#if UNITY_EDITOR
if (!ScenePlaybackDetector.IsPlaying) { EditorThreadDispatcher.Instance.UnsafeInvoke(action, state); return; }
#endif
try
{
action(state);
}
catch (Exception ex)
{
var dispatcher = MainThreadDispatcher.Instance;
if (dispatcher != null)
{
dispatcher.unhandledExceptionCallback(ex);
}
}
}
/// <summary>ThreadSafe StartCoroutine.</summary>
public static void SendStartCoroutine(IEnumerator routine)
{
if (mainThreadToken != null)
{
StartCoroutine(routine);
}
else
{
#if UNITY_EDITOR
// call from other thread
if (!ScenePlaybackDetector.IsPlaying) { EditorThreadDispatcher.Instance.PseudoStartCoroutine(routine); return; }
#endif
var dispatcher = Instance;
if (!isQuitting && !object.ReferenceEquals(dispatcher, null))
{
dispatcher.queueWorker.Enqueue(_ =>
{
var dispacher2 = Instance;
if (dispacher2 != null)
{
(dispacher2 as MonoBehaviour).StartCoroutine(routine);
}
}, null);
}
}
}
public static void StartUpdateMicroCoroutine(IEnumerator routine)
{
#if UNITY_EDITOR
if (!ScenePlaybackDetector.IsPlaying) { EditorThreadDispatcher.Instance.PseudoStartCoroutine(routine); return; }
#endif
var dispatcher = Instance;
if (dispatcher != null)
{
dispatcher.updateMicroCoroutine.AddCoroutine(routine);
}
}
public static void StartFixedUpdateMicroCoroutine(IEnumerator routine)
{
#if UNITY_EDITOR
if (!ScenePlaybackDetector.IsPlaying) { EditorThreadDispatcher.Instance.PseudoStartCoroutine(routine); return; }
#endif
var dispatcher = Instance;
if (dispatcher != null)
{
dispatcher.fixedUpdateMicroCoroutine.AddCoroutine(routine);
}
}
public static void StartEndOfFrameMicroCoroutine(IEnumerator routine)
{
#if UNITY_EDITOR
if (!ScenePlaybackDetector.IsPlaying) { EditorThreadDispatcher.Instance.PseudoStartCoroutine(routine); return; }
#endif
var dispatcher = Instance;
if (dispatcher != null)
{
dispatcher.endOfFrameMicroCoroutine.AddCoroutine(routine);
}
}
new public static Coroutine StartCoroutine(IEnumerator routine)
{
#if UNITY_EDITOR
if (!ScenePlaybackDetector.IsPlaying) { EditorThreadDispatcher.Instance.PseudoStartCoroutine(routine); return null; }
#endif
var dispatcher = Instance;
if (dispatcher != null)
{
return (dispatcher as MonoBehaviour).StartCoroutine(routine);
}
else
{
return null;
}
}
public static void RegisterUnhandledExceptionCallback(Action<Exception> exceptionCallback)
{
if (exceptionCallback == null)
{
// do nothing
Instance.unhandledExceptionCallback = Stubs<Exception>.Ignore;
}
else
{
Instance.unhandledExceptionCallback = exceptionCallback;
}
}
ThreadSafeQueueWorker queueWorker = new ThreadSafeQueueWorker();
Action<Exception> unhandledExceptionCallback = ex => Debug.LogException(ex); // default
MicroCoroutine updateMicroCoroutine = null;
MicroCoroutine fixedUpdateMicroCoroutine = null;
MicroCoroutine endOfFrameMicroCoroutine = null;
static MainThreadDispatcher instance;
static bool initialized;
static bool isQuitting = false;
public static string InstanceName
{
get
{
if (instance == null)
{
throw new NullReferenceException("MainThreadDispatcher is not initialized.");
}
return instance.name;
}
}
public static bool IsInitialized
{
get { return initialized && instance != null; }
}
[ThreadStatic]
static object mainThreadToken;
static MainThreadDispatcher Instance
{
get
{
Initialize();
return instance;
}
}
public static void Initialize()
{
if (!initialized)
{
#if UNITY_EDITOR
// Don't try to add a GameObject when the scene is not playing. Only valid in the Editor, EditorView.
if (!ScenePlaybackDetector.IsPlaying) return;
#endif
MainThreadDispatcher dispatcher = null;
try
{
dispatcher = GameObject.FindObjectOfType<MainThreadDispatcher>();
}
catch
{
// Throw exception when calling from a worker thread.
var ex = new Exception("UniRx requires a MainThreadDispatcher component created on the main thread. Make sure it is added to the scene before calling UniRx from a worker thread.");
UnityEngine.Debug.LogException(ex);
throw ex;
}
if (isQuitting)
{
// don't create new instance after quitting
// avoid "Some objects were not cleaned up when closing the scene find target" error.
return;
}
if (dispatcher == null)
{
// awake call immediately from UnityEngine
new GameObject("MainThreadDispatcher").AddComponent<MainThreadDispatcher>();
}
else
{
dispatcher.Awake(); // force awake
}
}
}
public static bool IsInMainThread
{
get
{
return (mainThreadToken != null);
}
}
void Awake()
{
if (instance == null)
{
instance = this;
mainThreadToken = new object();
initialized = true;
updateMicroCoroutine = new MicroCoroutine(ex => unhandledExceptionCallback(ex));
fixedUpdateMicroCoroutine = new MicroCoroutine(ex => unhandledExceptionCallback(ex));
endOfFrameMicroCoroutine = new MicroCoroutine(ex => unhandledExceptionCallback(ex));
StartCoroutine(RunUpdateMicroCoroutine());
StartCoroutine(RunFixedUpdateMicroCoroutine());
StartCoroutine(RunEndOfFrameMicroCoroutine());
DontDestroyOnLoad(gameObject);
}
else
{
if (this != instance)
{
if (cullingMode == CullingMode.Self)
{
// Try to destroy this dispatcher if there's already one in the scene.
Debug.LogWarning("There is already a MainThreadDispatcher in the scene. Removing myself...");
DestroyDispatcher(this);
}
else if (cullingMode == CullingMode.All)
{
Debug.LogWarning("There is already a MainThreadDispatcher in the scene. Cleaning up all excess dispatchers...");
CullAllExcessDispatchers();
}
else
{
Debug.LogWarning("There is already a MainThreadDispatcher in the scene.");
}
}
}
}
IEnumerator RunUpdateMicroCoroutine()
{
while (true)
{
yield return null;
updateMicroCoroutine.Run();
}
}
IEnumerator RunFixedUpdateMicroCoroutine()
{
while (true)
{
yield return YieldInstructionCache.WaitForFixedUpdate;
fixedUpdateMicroCoroutine.Run();
}
}
IEnumerator RunEndOfFrameMicroCoroutine()
{
while (true)
{
yield return YieldInstructionCache.WaitForEndOfFrame;
endOfFrameMicroCoroutine.Run();
}
}
static void DestroyDispatcher(MainThreadDispatcher aDispatcher)
{
if (aDispatcher != instance)
{
// Try to remove game object if it's empty
var components = aDispatcher.gameObject.GetComponents<Component>();
if (aDispatcher.gameObject.transform.childCount == 0 && components.Length == 2)
{
if (components[0] is Transform && components[1] is MainThreadDispatcher)
{
Destroy(aDispatcher.gameObject);
}
}
else
{
// Remove component
MonoBehaviour.Destroy(aDispatcher);
}
}
}
public static void CullAllExcessDispatchers()
{
var dispatchers = GameObject.FindObjectsOfType<MainThreadDispatcher>();
for (int i = 0; i < dispatchers.Length; i++)
{
DestroyDispatcher(dispatchers[i]);
}
}
void OnDestroy()
{
if (instance == this)
{
instance = GameObject.FindObjectOfType<MainThreadDispatcher>();
initialized = instance != null;
/*
// Although `this` still refers to a gameObject, it won't be found.
var foundDispatcher = GameObject.FindObjectOfType<MainThreadDispatcher>();
if (foundDispatcher != null)
{
// select another game object
Debug.Log("new instance: " + foundDispatcher.name);
instance = foundDispatcher;
initialized = true;
}
*/
}
}
void Update()
{
if (update != null)
{
try
{
update.OnNext(Unit.Default);
}
catch (Exception ex)
{
unhandledExceptionCallback(ex);
}
}
queueWorker.ExecuteAll(unhandledExceptionCallback);
}
// for Lifecycle Management
Subject<Unit> update;
public static IObservable<Unit> UpdateAsObservable()
{
return Instance.update ?? (Instance.update = new Subject<Unit>());
}
Subject<Unit> lateUpdate;
void LateUpdate()
{
if (lateUpdate != null) lateUpdate.OnNext(Unit.Default);
}
public static IObservable<Unit> LateUpdateAsObservable()
{
return Instance.lateUpdate ?? (Instance.lateUpdate = new Subject<Unit>());
}
Subject<bool> onApplicationFocus;
void OnApplicationFocus(bool focus)
{
if (onApplicationFocus != null) onApplicationFocus.OnNext(focus);
}
public static IObservable<bool> OnApplicationFocusAsObservable()
{
return Instance.onApplicationFocus ?? (Instance.onApplicationFocus = new Subject<bool>());
}
Subject<bool> onApplicationPause;
void OnApplicationPause(bool pause)
{
if (onApplicationPause != null) onApplicationPause.OnNext(pause);
}
public static IObservable<bool> OnApplicationPauseAsObservable()
{
return Instance.onApplicationPause ?? (Instance.onApplicationPause = new Subject<bool>());
}
Subject<Unit> onApplicationQuit;
void OnApplicationQuit()
{
isQuitting = true;
if (onApplicationQuit != null) onApplicationQuit.OnNext(Unit.Default);
}
public static IObservable<Unit> OnApplicationQuitAsObservable()
{
return Instance.onApplicationQuit ?? (Instance.onApplicationQuit = new Subject<Unit>());
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c3cd207057515c4438a31a6a7b548fe7
timeCreated: 1465903910
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: -16000
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,579 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using UnityEngine;
namespace UniRx
{
#if UniRxLibrary
public static partial class SchedulerUnity
{
#else
public static partial class Scheduler
{
public static void SetDefaultForUnity()
{
Scheduler.DefaultSchedulers.ConstantTimeOperations = Scheduler.Immediate;
Scheduler.DefaultSchedulers.TailRecursion = Scheduler.Immediate;
Scheduler.DefaultSchedulers.Iteration = Scheduler.CurrentThread;
Scheduler.DefaultSchedulers.TimeBasedOperations = MainThread;
Scheduler.DefaultSchedulers.AsyncConversions = Scheduler.ThreadPool;
}
#endif
static IScheduler mainThread;
/// <summary>
/// Unity native MainThread Queue Scheduler. Run on mainthread and delayed on coroutine update loop, elapsed time is calculated based on Time.time.
/// </summary>
public static IScheduler MainThread
{
get
{
return mainThread ?? (mainThread = new MainThreadScheduler());
}
}
static IScheduler mainThreadIgnoreTimeScale;
/// <summary>
/// Another MainThread scheduler, delay elapsed time is calculated based on Time.unscaledDeltaTime.
/// </summary>
public static IScheduler MainThreadIgnoreTimeScale
{
get
{
return mainThreadIgnoreTimeScale ?? (mainThreadIgnoreTimeScale = new IgnoreTimeScaleMainThreadScheduler());
}
}
static IScheduler mainThreadFixedUpdate;
/// <summary>
/// Run on fixed update mainthread, delay elapsed time is calculated based on Time.fixedTime.
/// </summary>
public static IScheduler MainThreadFixedUpdate
{
get
{
return mainThreadFixedUpdate ?? (mainThreadFixedUpdate = new FixedUpdateMainThreadScheduler());
}
}
static IScheduler mainThreadEndOfFrame;
/// <summary>
/// Run on end of frame mainthread, delay elapsed time is calculated based on Time.deltaTime.
/// </summary>
public static IScheduler MainThreadEndOfFrame
{
get
{
return mainThreadEndOfFrame ?? (mainThreadEndOfFrame = new EndOfFrameMainThreadScheduler());
}
}
class MainThreadScheduler : IScheduler, ISchedulerPeriodic, ISchedulerQueueing
{
readonly Action<object> scheduleAction;
public MainThreadScheduler()
{
MainThreadDispatcher.Initialize();
scheduleAction = new Action<object>(Schedule);
}
// delay action is run in StartCoroutine
// Okay to action run synchronous and guaranteed run on MainThread
IEnumerator DelayAction(TimeSpan dueTime, Action action, ICancelable cancellation)
{
// zero == every frame
if (dueTime == TimeSpan.Zero)
{
yield return null; // not immediately, run next frame
}
else
{
yield return new WaitForSeconds((float)dueTime.TotalSeconds);
}
if (cancellation.IsDisposed) yield break;
MainThreadDispatcher.UnsafeSend(action);
}
IEnumerator PeriodicAction(TimeSpan period, Action action, ICancelable cancellation)
{
// zero == every frame
if (period == TimeSpan.Zero)
{
while (true)
{
yield return null; // not immediately, run next frame
if (cancellation.IsDisposed) yield break;
MainThreadDispatcher.UnsafeSend(action);
}
}
else
{
var seconds = (float)(period.TotalMilliseconds / 1000.0);
var yieldInstruction = new WaitForSeconds(seconds); // cache single instruction object
while (true)
{
yield return yieldInstruction;
if (cancellation.IsDisposed) yield break;
MainThreadDispatcher.UnsafeSend(action);
}
}
}
public DateTimeOffset Now
{
get { return Scheduler.Now; }
}
void Schedule(object state)
{
var t = (Tuple<BooleanDisposable, Action>)state;
if (!t.Item1.IsDisposed)
{
t.Item2();
}
}
public IDisposable Schedule(Action action)
{
var d = new BooleanDisposable();
MainThreadDispatcher.Post(scheduleAction, Tuple.Create(d, action));
return d;
}
public IDisposable Schedule(DateTimeOffset dueTime, Action action)
{
return Schedule(dueTime - Now, action);
}
public IDisposable Schedule(TimeSpan dueTime, Action action)
{
var d = new BooleanDisposable();
var time = Scheduler.Normalize(dueTime);
MainThreadDispatcher.SendStartCoroutine(DelayAction(time, action, d));
return d;
}
public IDisposable SchedulePeriodic(TimeSpan period, Action action)
{
var d = new BooleanDisposable();
var time = Scheduler.Normalize(period);
MainThreadDispatcher.SendStartCoroutine(PeriodicAction(time, action, d));
return d;
}
void ScheduleQueueing<T>(object state)
{
var t = (Tuple<ICancelable, T, Action<T>>)state;
if (!t.Item1.IsDisposed)
{
t.Item3(t.Item2);
}
}
public void ScheduleQueueing<T>(ICancelable cancel, T state, Action<T> action)
{
MainThreadDispatcher.Post(QueuedAction<T>.Instance, Tuple.Create(cancel, state, action));
}
static class QueuedAction<T>
{
public static readonly Action<object> Instance = new Action<object>(Invoke);
public static void Invoke(object state)
{
var t = (Tuple<ICancelable, T, Action<T>>)state;
if (!t.Item1.IsDisposed)
{
t.Item3(t.Item2);
}
}
}
}
class IgnoreTimeScaleMainThreadScheduler : IScheduler, ISchedulerPeriodic, ISchedulerQueueing
{
readonly Action<object> scheduleAction;
public IgnoreTimeScaleMainThreadScheduler()
{
MainThreadDispatcher.Initialize();
scheduleAction = new Action<object>(Schedule);
}
IEnumerator DelayAction(TimeSpan dueTime, Action action, ICancelable cancellation)
{
if (dueTime == TimeSpan.Zero)
{
yield return null;
if (cancellation.IsDisposed) yield break;
MainThreadDispatcher.UnsafeSend(action);
}
else
{
var elapsed = 0f;
var dt = (float)dueTime.TotalSeconds;
while (true)
{
yield return null;
if (cancellation.IsDisposed) break;
elapsed += Time.unscaledDeltaTime;
if (elapsed >= dt)
{
MainThreadDispatcher.UnsafeSend(action);
break;
}
}
}
}
IEnumerator PeriodicAction(TimeSpan period, Action action, ICancelable cancellation)
{
// zero == every frame
if (period == TimeSpan.Zero)
{
while (true)
{
yield return null; // not immediately, run next frame
if (cancellation.IsDisposed) yield break;
MainThreadDispatcher.UnsafeSend(action);
}
}
else
{
var elapsed = 0f;
var dt = (float)period.TotalSeconds;
while (true)
{
yield return null;
if (cancellation.IsDisposed) break;
elapsed += Time.unscaledDeltaTime;
if (elapsed >= dt)
{
MainThreadDispatcher.UnsafeSend(action);
elapsed = 0;
}
}
}
}
public DateTimeOffset Now
{
get { return Scheduler.Now; }
}
void Schedule(object state)
{
var t = (Tuple<BooleanDisposable, Action>)state;
if (!t.Item1.IsDisposed)
{
t.Item2();
}
}
public IDisposable Schedule(Action action)
{
var d = new BooleanDisposable();
MainThreadDispatcher.Post(scheduleAction, Tuple.Create(d, action));
return d;
}
public IDisposable Schedule(DateTimeOffset dueTime, Action action)
{
return Schedule(dueTime - Now, action);
}
public IDisposable Schedule(TimeSpan dueTime, Action action)
{
var d = new BooleanDisposable();
var time = Scheduler.Normalize(dueTime);
MainThreadDispatcher.SendStartCoroutine(DelayAction(time, action, d));
return d;
}
public IDisposable SchedulePeriodic(TimeSpan period, Action action)
{
var d = new BooleanDisposable();
var time = Scheduler.Normalize(period);
MainThreadDispatcher.SendStartCoroutine(PeriodicAction(time, action, d));
return d;
}
public void ScheduleQueueing<T>(ICancelable cancel, T state, Action<T> action)
{
MainThreadDispatcher.Post(QueuedAction<T>.Instance, Tuple.Create(cancel, state, action));
}
static class QueuedAction<T>
{
public static readonly Action<object> Instance = new Action<object>(Invoke);
public static void Invoke(object state)
{
var t = (Tuple<ICancelable, T, Action<T>>)state;
if (!t.Item1.IsDisposed)
{
t.Item3(t.Item2);
}
}
}
}
class FixedUpdateMainThreadScheduler : IScheduler, ISchedulerPeriodic, ISchedulerQueueing
{
public FixedUpdateMainThreadScheduler()
{
MainThreadDispatcher.Initialize();
}
IEnumerator ImmediateAction<T>(T state, Action<T> action, ICancelable cancellation)
{
yield return null;
if (cancellation.IsDisposed) yield break;
MainThreadDispatcher.UnsafeSend(action, state);
}
IEnumerator DelayAction(TimeSpan dueTime, Action action, ICancelable cancellation)
{
if (dueTime == TimeSpan.Zero)
{
yield return null;
if (cancellation.IsDisposed) yield break;
MainThreadDispatcher.UnsafeSend(action);
}
else
{
var startTime = Time.fixedTime;
var dt = (float)dueTime.TotalSeconds;
while (true)
{
yield return null;
if (cancellation.IsDisposed) break;
var elapsed = Time.fixedTime - startTime;
if (elapsed >= dt)
{
MainThreadDispatcher.UnsafeSend(action);
break;
}
}
}
}
IEnumerator PeriodicAction(TimeSpan period, Action action, ICancelable cancellation)
{
// zero == every frame
if (period == TimeSpan.Zero)
{
while (true)
{
yield return null;
if (cancellation.IsDisposed) yield break;
MainThreadDispatcher.UnsafeSend(action);
}
}
else
{
var startTime = Time.fixedTime;
var dt = (float)period.TotalSeconds;
while (true)
{
yield return null;
if (cancellation.IsDisposed) break;
var ft = Time.fixedTime;
var elapsed = ft - startTime;
if (elapsed >= dt)
{
MainThreadDispatcher.UnsafeSend(action);
startTime = ft;
}
}
}
}
public DateTimeOffset Now
{
get { return Scheduler.Now; }
}
public IDisposable Schedule(Action action)
{
return Schedule(TimeSpan.Zero, action);
}
public IDisposable Schedule(DateTimeOffset dueTime, Action action)
{
return Schedule(dueTime - Now, action);
}
public IDisposable Schedule(TimeSpan dueTime, Action action)
{
var d = new BooleanDisposable();
var time = Scheduler.Normalize(dueTime);
MainThreadDispatcher.StartFixedUpdateMicroCoroutine(DelayAction(time, action, d));
return d;
}
public IDisposable SchedulePeriodic(TimeSpan period, Action action)
{
var d = new BooleanDisposable();
var time = Scheduler.Normalize(period);
MainThreadDispatcher.StartFixedUpdateMicroCoroutine(PeriodicAction(time, action, d));
return d;
}
public void ScheduleQueueing<T>(ICancelable cancel, T state, Action<T> action)
{
MainThreadDispatcher.StartFixedUpdateMicroCoroutine(ImmediateAction(state, action, cancel));
}
}
class EndOfFrameMainThreadScheduler : IScheduler, ISchedulerPeriodic, ISchedulerQueueing
{
public EndOfFrameMainThreadScheduler()
{
MainThreadDispatcher.Initialize();
}
IEnumerator ImmediateAction<T>(T state, Action<T> action, ICancelable cancellation)
{
yield return null;
if (cancellation.IsDisposed) yield break;
MainThreadDispatcher.UnsafeSend(action, state);
}
IEnumerator DelayAction(TimeSpan dueTime, Action action, ICancelable cancellation)
{
if (dueTime == TimeSpan.Zero)
{
yield return null;
if (cancellation.IsDisposed) yield break;
MainThreadDispatcher.UnsafeSend(action);
}
else
{
var elapsed = 0f;
var dt = (float)dueTime.TotalSeconds;
while (true)
{
yield return null;
if (cancellation.IsDisposed) break;
elapsed += Time.deltaTime;
if (elapsed >= dt)
{
MainThreadDispatcher.UnsafeSend(action);
break;
}
}
}
}
IEnumerator PeriodicAction(TimeSpan period, Action action, ICancelable cancellation)
{
// zero == every frame
if (period == TimeSpan.Zero)
{
while (true)
{
yield return null;
if (cancellation.IsDisposed) yield break;
MainThreadDispatcher.UnsafeSend(action);
}
}
else
{
var elapsed = 0f;
var dt = (float)period.TotalSeconds;
while (true)
{
yield return null;
if (cancellation.IsDisposed) break;
elapsed += Time.deltaTime;
if (elapsed >= dt)
{
MainThreadDispatcher.UnsafeSend(action);
elapsed = 0;
}
}
}
}
public DateTimeOffset Now
{
get { return Scheduler.Now; }
}
public IDisposable Schedule(Action action)
{
return Schedule(TimeSpan.Zero, action);
}
public IDisposable Schedule(DateTimeOffset dueTime, Action action)
{
return Schedule(dueTime - Now, action);
}
public IDisposable Schedule(TimeSpan dueTime, Action action)
{
var d = new BooleanDisposable();
var time = Scheduler.Normalize(dueTime);
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(DelayAction(time, action, d));
return d;
}
public IDisposable SchedulePeriodic(TimeSpan period, Action action)
{
var d = new BooleanDisposable();
var time = Scheduler.Normalize(period);
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(PeriodicAction(time, action, d));
return d;
}
public void ScheduleQueueing<T>(ICancelable cancel, T state, Action<T> action)
{
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(ImmediateAction(state, action, cancel));
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f141c5dc72b97084a85631367a946ee8
timeCreated: 1455373902
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c6ef0a186b9ceaf41af7f2a9f4006216
timeCreated: 1455373901
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,442 @@
using System;
using System.Collections;
using UnityEngine;
#if !UniRxLibrary
using ObservableUnity = UniRx.Observable;
#endif
#if UNITY_2018_3_OR_NEWER
#pragma warning disable CS0618
#endif
namespace UniRx
{
using System.Threading;
#if !(UNITY_METRO || UNITY_WP8) && (UNITY_4_4 || UNITY_4_3 || UNITY_4_2 || UNITY_4_1 || UNITY_4_0_1 || UNITY_4_0 || UNITY_3_5 || UNITY_3_4 || UNITY_3_3 || UNITY_3_2 || UNITY_3_1 || UNITY_3_0_0 || UNITY_3_0 || UNITY_2_6_1 || UNITY_2_6)
// Fallback for Unity versions below 4.5
using Hash = System.Collections.Hashtable;
using HashEntry = System.Collections.DictionaryEntry;
#else
// Unity 4.5 release notes:
// WWW: deprecated 'WWW(string url, byte[] postData, Hashtable headers)',
// use 'public WWW(string url, byte[] postData, Dictionary<string, string> headers)' instead.
using Hash = System.Collections.Generic.Dictionary<string, string>;
using HashEntry = System.Collections.Generic.KeyValuePair<string, string>;
#endif
#if UNITY_2018_3_OR_NEWER
[Obsolete("Use UnityWebRequest, a fully featured replacement which is more efficient and has additional features")]
#endif
public static partial class ObservableWWW
{
public static IObservable<string> Get(string url, Hash headers = null, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<string>((observer, cancellation) => FetchText(new WWW(url, null, (headers ?? new Hash())), observer, progress, cancellation));
}
public static IObservable<byte[]> GetAndGetBytes(string url, Hash headers = null, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<byte[]>((observer, cancellation) => FetchBytes(new WWW(url, null, (headers ?? new Hash())), observer, progress, cancellation));
}
public static IObservable<WWW> GetWWW(string url, Hash headers = null, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<WWW>((observer, cancellation) => Fetch(new WWW(url, null, (headers ?? new Hash())), observer, progress, cancellation));
}
public static IObservable<string> Post(string url, byte[] postData, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<string>((observer, cancellation) => FetchText(new WWW(url, postData), observer, progress, cancellation));
}
public static IObservable<string> Post(string url, byte[] postData, Hash headers, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<string>((observer, cancellation) => FetchText(new WWW(url, postData, headers), observer, progress, cancellation));
}
public static IObservable<string> Post(string url, WWWForm content, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<string>((observer, cancellation) => FetchText(new WWW(url, content), observer, progress, cancellation));
}
public static IObservable<string> Post(string url, WWWForm content, Hash headers, IProgress<float> progress = null)
{
var contentHeaders = content.headers;
return ObservableUnity.FromCoroutine<string>((observer, cancellation) => FetchText(new WWW(url, content.data, MergeHash(contentHeaders, headers)), observer, progress, cancellation));
}
public static IObservable<byte[]> PostAndGetBytes(string url, byte[] postData, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<byte[]>((observer, cancellation) => FetchBytes(new WWW(url, postData), observer, progress, cancellation));
}
public static IObservable<byte[]> PostAndGetBytes(string url, byte[] postData, Hash headers, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<byte[]>((observer, cancellation) => FetchBytes(new WWW(url, postData, headers), observer, progress, cancellation));
}
public static IObservable<byte[]> PostAndGetBytes(string url, WWWForm content, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<byte[]>((observer, cancellation) => FetchBytes(new WWW(url, content), observer, progress, cancellation));
}
public static IObservable<byte[]> PostAndGetBytes(string url, WWWForm content, Hash headers, IProgress<float> progress = null)
{
var contentHeaders = content.headers;
return ObservableUnity.FromCoroutine<byte[]>((observer, cancellation) => FetchBytes(new WWW(url, content.data, MergeHash(contentHeaders, headers)), observer, progress, cancellation));
}
public static IObservable<WWW> PostWWW(string url, byte[] postData, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<WWW>((observer, cancellation) => Fetch(new WWW(url, postData), observer, progress, cancellation));
}
public static IObservable<WWW> PostWWW(string url, byte[] postData, Hash headers, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<WWW>((observer, cancellation) => Fetch(new WWW(url, postData, headers), observer, progress, cancellation));
}
public static IObservable<WWW> PostWWW(string url, WWWForm content, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<WWW>((observer, cancellation) => Fetch(new WWW(url, content), observer, progress, cancellation));
}
public static IObservable<WWW> PostWWW(string url, WWWForm content, Hash headers, IProgress<float> progress = null)
{
var contentHeaders = content.headers;
return ObservableUnity.FromCoroutine<WWW>((observer, cancellation) => Fetch(new WWW(url, content.data, MergeHash(contentHeaders, headers)), observer, progress, cancellation));
}
public static IObservable<AssetBundle> LoadFromCacheOrDownload(string url, int version, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<AssetBundle>((observer, cancellation) => FetchAssetBundle(WWW.LoadFromCacheOrDownload(url, version), observer, progress, cancellation));
}
public static IObservable<AssetBundle> LoadFromCacheOrDownload(string url, int version, uint crc, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<AssetBundle>((observer, cancellation) => FetchAssetBundle(WWW.LoadFromCacheOrDownload(url, version, crc), observer, progress, cancellation));
}
// over Unity5 supports Hash128
#if !(UNITY_4_7 || UNITY_4_6 || UNITY_4_5 || UNITY_4_4 || UNITY_4_3 || UNITY_4_2 || UNITY_4_1 || UNITY_4_0_1 || UNITY_4_0 || UNITY_3_5 || UNITY_3_4 || UNITY_3_3 || UNITY_3_2 || UNITY_3_1 || UNITY_3_0_0 || UNITY_3_0 || UNITY_2_6_1 || UNITY_2_6)
public static IObservable<AssetBundle> LoadFromCacheOrDownload(string url, Hash128 hash128, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<AssetBundle>((observer, cancellation) => FetchAssetBundle(WWW.LoadFromCacheOrDownload(url, hash128), observer, progress, cancellation));
}
public static IObservable<AssetBundle> LoadFromCacheOrDownload(string url, Hash128 hash128, uint crc, IProgress<float> progress = null)
{
return ObservableUnity.FromCoroutine<AssetBundle>((observer, cancellation) => FetchAssetBundle(WWW.LoadFromCacheOrDownload(url, hash128, crc), observer, progress, cancellation));
}
#endif
// over 4.5, Hash define is Dictionary.
// below Unity 4.5, WWW only supports Hashtable.
// Unity 4.5, 4.6 WWW supports Dictionary and [Obsolete]Hashtable but WWWForm.content is Hashtable.
// Unity 5.0 WWW only supports Dictionary and WWWForm.content is also Dictionary.
#if !(UNITY_METRO || UNITY_WP8) && (UNITY_4_5 || UNITY_4_6 || UNITY_4_7)
static Hash MergeHash(Hashtable wwwFormHeaders, Hash externalHeaders)
{
var newHeaders = new Hash();
foreach (DictionaryEntry item in wwwFormHeaders)
{
newHeaders[item.Key.ToString()] = item.Value.ToString();
}
foreach (HashEntry item in externalHeaders)
{
newHeaders[item.Key] = item.Value;
}
return newHeaders;
}
#else
static Hash MergeHash(Hash wwwFormHeaders, Hash externalHeaders)
{
foreach (HashEntry item in externalHeaders)
{
wwwFormHeaders[item.Key] = item.Value;
}
return wwwFormHeaders;
}
#endif
static IEnumerator Fetch(WWW www, IObserver<WWW> observer, IProgress<float> reportProgress, CancellationToken cancel)
{
using (www)
{
if (reportProgress != null)
{
while (!www.isDone && !cancel.IsCancellationRequested)
{
try
{
reportProgress.Report(www.progress);
}
catch (Exception ex)
{
observer.OnError(ex);
yield break;
}
yield return null;
}
}
else
{
if (!www.isDone)
{
yield return www;
}
}
if (cancel.IsCancellationRequested)
{
yield break;
}
if (reportProgress != null)
{
try
{
reportProgress.Report(www.progress);
}
catch (Exception ex)
{
observer.OnError(ex);
yield break;
}
}
if (!string.IsNullOrEmpty(www.error))
{
observer.OnError(new WWWErrorException(www, www.text));
}
else
{
observer.OnNext(www);
observer.OnCompleted();
}
}
}
static IEnumerator FetchText(WWW www, IObserver<string> observer, IProgress<float> reportProgress, CancellationToken cancel)
{
using (www)
{
if (reportProgress != null)
{
while (!www.isDone && !cancel.IsCancellationRequested)
{
try
{
reportProgress.Report(www.progress);
}
catch (Exception ex)
{
observer.OnError(ex);
yield break;
}
yield return null;
}
}
else
{
if (!www.isDone)
{
yield return www;
}
}
if (cancel.IsCancellationRequested)
{
yield break;
}
if (reportProgress != null)
{
try
{
reportProgress.Report(www.progress);
}
catch (Exception ex)
{
observer.OnError(ex);
yield break;
}
}
if (!string.IsNullOrEmpty(www.error))
{
observer.OnError(new WWWErrorException(www, www.text));
}
else
{
observer.OnNext(www.text);
observer.OnCompleted();
}
}
}
static IEnumerator FetchBytes(WWW www, IObserver<byte[]> observer, IProgress<float> reportProgress, CancellationToken cancel)
{
using (www)
{
if (reportProgress != null)
{
while (!www.isDone && !cancel.IsCancellationRequested)
{
try
{
reportProgress.Report(www.progress);
}
catch (Exception ex)
{
observer.OnError(ex);
yield break;
}
yield return null;
}
}
else
{
if (!www.isDone)
{
yield return www;
}
}
if (cancel.IsCancellationRequested)
{
yield break;
}
if (reportProgress != null)
{
try
{
reportProgress.Report(www.progress);
}
catch (Exception ex)
{
observer.OnError(ex);
yield break;
}
}
if (!string.IsNullOrEmpty(www.error))
{
observer.OnError(new WWWErrorException(www, www.text));
}
else
{
observer.OnNext(www.bytes);
observer.OnCompleted();
}
}
}
static IEnumerator FetchAssetBundle(WWW www, IObserver<AssetBundle> observer, IProgress<float> reportProgress, CancellationToken cancel)
{
using (www)
{
if (reportProgress != null)
{
while (!www.isDone && !cancel.IsCancellationRequested)
{
try
{
reportProgress.Report(www.progress);
}
catch (Exception ex)
{
observer.OnError(ex);
yield break;
}
yield return null;
}
}
else
{
if (!www.isDone)
{
yield return www;
}
}
if (cancel.IsCancellationRequested)
{
yield break;
}
if (reportProgress != null)
{
try
{
reportProgress.Report(www.progress);
}
catch (Exception ex)
{
observer.OnError(ex);
yield break;
}
}
if (!string.IsNullOrEmpty(www.error))
{
observer.OnError(new WWWErrorException(www, ""));
}
else
{
observer.OnNext(www.assetBundle);
observer.OnCompleted();
}
}
}
}
public class WWWErrorException : Exception
{
public string RawErrorMessage { get; private set; }
public bool HasResponse { get; private set; }
public string Text { get; private set; }
public System.Net.HttpStatusCode StatusCode { get; private set; }
public System.Collections.Generic.Dictionary<string, string> ResponseHeaders { get; private set; }
public WWW WWW { get; private set; }
// cache the text because if www was disposed, can't access it.
public WWWErrorException(WWW www, string text)
{
this.WWW = www;
this.RawErrorMessage = www.error;
this.ResponseHeaders = www.responseHeaders;
this.HasResponse = false;
this.Text = text;
var splitted = RawErrorMessage.Split(' ', ':');
if (splitted.Length != 0)
{
int statusCode;
if (int.TryParse(splitted[0], out statusCode))
{
this.HasResponse = true;
this.StatusCode = (System.Net.HttpStatusCode)statusCode;
}
}
}
public override string ToString()
{
var text = this.Text;
if (string.IsNullOrEmpty(text))
{
return RawErrorMessage;
}
else
{
return RawErrorMessage + " " + text;
}
}
}
}
#if UNITY_2018_3_OR_NEWER
#pragma warning restore CS0618
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ba71e5544e233dd4b83d4c5a6c696d05
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,264 @@
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;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8741793924a6c2f4ea22ba27031d531f
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4d126dc4a05228e418759d57f7661329
folderAsset: yes
timeCreated: 1455373896
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,282 @@
using System;
using System.Collections.Generic;
namespace UniRx.Operators
{
internal class BatchFrameObservable<T> : OperatorObservableBase<IList<T>>
{
readonly IObservable<T> source;
readonly int frameCount;
readonly FrameCountType frameCountType;
public BatchFrameObservable(IObservable<T> source, int frameCount, FrameCountType frameCountType)
: base(source.IsRequiredSubscribeOnCurrentThread())
{
this.source = source;
this.frameCount = frameCount;
this.frameCountType = frameCountType;
}
protected override IDisposable SubscribeCore(IObserver<IList<T>> observer, IDisposable cancel)
{
return new BatchFrame(this, observer, cancel).Run();
}
class BatchFrame : OperatorObserverBase<T, IList<T>>
{
readonly BatchFrameObservable<T> parent;
readonly object gate = new object();
readonly BooleanDisposable cancellationToken = new BooleanDisposable();
readonly System.Collections.IEnumerator timer;
bool isRunning;
bool isCompleted;
List<T> list;
public BatchFrame(BatchFrameObservable<T> parent, IObserver<IList<T>> observer, IDisposable cancel) : base(observer, cancel)
{
this.parent = parent;
this.timer = new ReusableEnumerator(this);
}
public IDisposable Run()
{
list = new List<T>();
var sourceSubscription = parent.source.Subscribe(this);
return StableCompositeDisposable.Create(sourceSubscription, cancellationToken);
}
public override void OnNext(T value)
{
lock (gate)
{
if (isCompleted) return;
list.Add(value);
if (!isRunning)
{
isRunning = true;
timer.Reset(); // reuse
switch (parent.frameCountType)
{
case FrameCountType.Update:
MainThreadDispatcher.StartUpdateMicroCoroutine(timer);
break;
case FrameCountType.FixedUpdate:
MainThreadDispatcher.StartFixedUpdateMicroCoroutine(timer);
break;
case FrameCountType.EndOfFrame:
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(timer);
break;
default:
break;
}
}
}
}
public override void OnError(Exception error)
{
try { observer.OnError(error); } finally { Dispose(); }
}
public override void OnCompleted()
{
List<T> currentList;
lock (gate)
{
isCompleted = true;
currentList = list;
}
if (currentList.Count != 0)
{
observer.OnNext(currentList);
}
try { observer.OnCompleted(); } finally { Dispose(); }
}
// reuse, no gc allocate
class ReusableEnumerator : System.Collections.IEnumerator
{
readonly BatchFrame parent;
int currentFrame;
public ReusableEnumerator(BatchFrame parent)
{
this.parent = parent;
}
public object Current
{
get { return null; }
}
public bool MoveNext()
{
if (parent.cancellationToken.IsDisposed) return false;
List<T> currentList;
lock (parent.gate)
{
if (currentFrame++ == parent.parent.frameCount)
{
if (parent.isCompleted) return false;
currentList = parent.list;
parent.list = new List<T>();
parent.isRunning = false;
// exit lock
}
else
{
return true;
}
}
parent.observer.OnNext(currentList);
return false;
}
public void Reset()
{
currentFrame = 0;
}
}
}
}
internal class BatchFrameObservable : OperatorObservableBase<Unit>
{
readonly IObservable<Unit> source;
readonly int frameCount;
readonly FrameCountType frameCountType;
public BatchFrameObservable(IObservable<Unit> source, int frameCount, FrameCountType frameCountType)
: base(source.IsRequiredSubscribeOnCurrentThread())
{
this.source = source;
this.frameCount = frameCount;
this.frameCountType = frameCountType;
}
protected override IDisposable SubscribeCore(IObserver<Unit> observer, IDisposable cancel)
{
return new BatchFrame(this, observer, cancel).Run();
}
class BatchFrame : OperatorObserverBase<Unit, Unit>
{
readonly BatchFrameObservable parent;
readonly object gate = new object();
readonly BooleanDisposable cancellationToken = new BooleanDisposable();
readonly System.Collections.IEnumerator timer;
bool isRunning;
bool isCompleted;
public BatchFrame(BatchFrameObservable parent, IObserver<Unit> observer, IDisposable cancel) : base(observer, cancel)
{
this.parent = parent;
this.timer = new ReusableEnumerator(this);
}
public IDisposable Run()
{
var sourceSubscription = parent.source.Subscribe(this);
return StableCompositeDisposable.Create(sourceSubscription, cancellationToken);
}
public override void OnNext(Unit value)
{
lock (gate)
{
if (!isRunning)
{
isRunning = true;
timer.Reset(); // reuse
switch (parent.frameCountType)
{
case FrameCountType.Update:
MainThreadDispatcher.StartUpdateMicroCoroutine(timer);
break;
case FrameCountType.FixedUpdate:
MainThreadDispatcher.StartFixedUpdateMicroCoroutine(timer);
break;
case FrameCountType.EndOfFrame:
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(timer);
break;
default:
break;
}
}
}
}
public override void OnError(Exception error)
{
try { observer.OnError(error); } finally { Dispose(); }
}
public override void OnCompleted()
{
bool running;
lock (gate)
{
running = isRunning;
isCompleted = true;
}
if (running)
{
observer.OnNext(Unit.Default);
}
try { observer.OnCompleted(); } finally { Dispose(); }
}
// reuse, no gc allocate
class ReusableEnumerator : System.Collections.IEnumerator
{
readonly BatchFrame parent;
int currentFrame;
public ReusableEnumerator(BatchFrame parent)
{
this.parent = parent;
}
public object Current
{
get { return null; }
}
public bool MoveNext()
{
if (parent.cancellationToken.IsDisposed) return false;
lock (parent.gate)
{
if (currentFrame++ == parent.parent.frameCount)
{
if (parent.isCompleted) return false;
parent.isRunning = false;
// exit lock
}
else
{
return true;
}
}
parent.observer.OnNext(Unit.Default);
return false;
}
public void Reset()
{
currentFrame = 0;
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 74a2b6b8c63d1144f914c7f0d6719a36
timeCreated: 1467771656
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,260 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace UniRx.Operators
{
internal class DelayFrameObservable<T> : OperatorObservableBase<T>
{
readonly IObservable<T> source;
readonly int frameCount;
readonly FrameCountType frameCountType;
public DelayFrameObservable(IObservable<T> source, int frameCount, FrameCountType frameCountType)
: base(source.IsRequiredSubscribeOnCurrentThread())
{
this.source = source;
this.frameCount = frameCount;
this.frameCountType = frameCountType;
}
protected override IDisposable SubscribeCore(IObserver<T> observer, IDisposable cancel)
{
return new DelayFrame(this, observer, cancel).Run();
}
class DelayFrame : OperatorObserverBase<T, T>
{
readonly DelayFrameObservable<T> parent;
readonly object gate = new object();
readonly QueuePool pool = new QueuePool();
int runningEnumeratorCount;
bool readyDrainEnumerator;
bool running;
IDisposable sourceSubscription;
Queue<T> currentQueueReference;
bool calledCompleted;
bool hasError;
Exception error;
BooleanDisposable cancelationToken;
public DelayFrame(DelayFrameObservable<T> parent, IObserver<T> observer, IDisposable cancel) : base(observer, cancel)
{
this.parent = parent;
}
public IDisposable Run()
{
cancelationToken = new BooleanDisposable();
var _sourceSubscription = new SingleAssignmentDisposable();
sourceSubscription = _sourceSubscription;
_sourceSubscription.Disposable = parent.source.Subscribe(this);
return StableCompositeDisposable.Create(cancelationToken, sourceSubscription);
}
IEnumerator DrainQueue(Queue<T> q, int frameCount)
{
lock (gate)
{
readyDrainEnumerator = false; // use next queue.
running = false;
}
while (!cancelationToken.IsDisposed && frameCount-- != 0)
{
yield return null;
}
try
{
if (q != null)
{
while (q.Count > 0 && !hasError)
{
if (cancelationToken.IsDisposed) break;
lock (gate)
{
running = true;
}
var value = q.Dequeue();
observer.OnNext(value);
lock (gate)
{
running = false;
}
}
if (q.Count == 0)
{
pool.Return(q);
}
}
if (hasError)
{
if (!cancelationToken.IsDisposed)
{
cancelationToken.Dispose();
try { observer.OnError(error); } finally { Dispose(); }
}
}
else if (calledCompleted)
{
lock (gate)
{
// not self only
if (runningEnumeratorCount != 1) yield break;
}
if (!cancelationToken.IsDisposed)
{
cancelationToken.Dispose();
try { observer.OnCompleted(); }
finally { Dispose(); }
}
}
}
finally
{
lock (gate)
{
runningEnumeratorCount--;
}
}
}
public override void OnNext(T value)
{
if (cancelationToken.IsDisposed) return;
Queue<T> targetQueue = null;
lock (gate)
{
if (!readyDrainEnumerator)
{
readyDrainEnumerator = true;
runningEnumeratorCount++;
targetQueue = currentQueueReference = pool.Get();
targetQueue.Enqueue(value);
}
else
{
if (currentQueueReference != null) // null - if doesn't start OnNext and start OnCompleted
{
currentQueueReference.Enqueue(value);
}
return;
}
}
switch (parent.frameCountType)
{
case FrameCountType.Update:
MainThreadDispatcher.StartUpdateMicroCoroutine(DrainQueue(targetQueue, parent.frameCount));
break;
case FrameCountType.FixedUpdate:
MainThreadDispatcher.StartFixedUpdateMicroCoroutine(DrainQueue(targetQueue, parent.frameCount));
break;
case FrameCountType.EndOfFrame:
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(DrainQueue(targetQueue, parent.frameCount));
break;
default:
throw new ArgumentException("Invalid FrameCountType:" + parent.frameCountType);
}
}
public override void OnError(Exception error)
{
sourceSubscription.Dispose(); // stop subscription
if (cancelationToken.IsDisposed) return;
lock (gate)
{
if (running)
{
hasError = true;
this.error = error;
return;
}
}
cancelationToken.Dispose();
try { base.observer.OnError(error); } finally { Dispose(); }
}
public override void OnCompleted()
{
sourceSubscription.Dispose(); // stop subscription
if (cancelationToken.IsDisposed) return;
lock (gate)
{
calledCompleted = true;
if (!readyDrainEnumerator)
{
readyDrainEnumerator = true;
runningEnumeratorCount++;
}
else
{
return;
}
}
switch (parent.frameCountType)
{
case FrameCountType.Update:
MainThreadDispatcher.StartUpdateMicroCoroutine(DrainQueue(null, parent.frameCount));
break;
case FrameCountType.FixedUpdate:
MainThreadDispatcher.StartFixedUpdateMicroCoroutine(DrainQueue(null, parent.frameCount));
break;
case FrameCountType.EndOfFrame:
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(DrainQueue(null, parent.frameCount));
break;
default:
throw new ArgumentException("Invalid FrameCountType:" + parent.frameCountType);
}
}
}
class QueuePool
{
readonly object gate = new object();
readonly Queue<Queue<T>> pool = new Queue<Queue<T>>(2);
public Queue<T> Get()
{
lock (gate)
{
if (pool.Count == 0)
{
return new Queue<T>(2);
}
else
{
return pool.Dequeue();
}
}
}
public void Return(Queue<T> q)
{
lock (gate)
{
pool.Enqueue(q);
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 868f75a703f1a944a801ab9c9b4512aa
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using System;
#if UniRxLibrary
using UnityObservable = UniRx.ObservableUnity;
#else
using UnityObservable = UniRx.Observable;
#endif
namespace UniRx.Operators
{
internal class DelayFrameSubscriptionObservable<T> : OperatorObservableBase<T>
{
readonly IObservable<T> source;
readonly int frameCount;
readonly FrameCountType frameCountType;
public DelayFrameSubscriptionObservable(IObservable<T> source, int frameCount, FrameCountType frameCountType)
: base(source.IsRequiredSubscribeOnCurrentThread())
{
this.source = source;
this.frameCount = frameCount;
this.frameCountType = frameCountType;
}
protected override IDisposable SubscribeCore(IObserver<T> observer, IDisposable cancel)
{
var d = new MultipleAssignmentDisposable();
d.Disposable = UnityObservable.TimerFrame(frameCount, frameCountType)
.SubscribeWithState3(observer, d, source, (_, o, disp, s) =>
{
disp.Disposable = s.Subscribe(o);
});
return d;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ecfef95eedf36c2448944fb8932f682c
timeCreated: 1455373902
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,52 @@
using System;
namespace UniRx.Operators
{
internal class FrameIntervalObservable<T> : OperatorObservableBase<UniRx.FrameInterval<T>>
{
readonly IObservable<T> source;
public FrameIntervalObservable(IObservable<T> source)
: base(source.IsRequiredSubscribeOnCurrentThread())
{
this.source = source;
}
protected override IDisposable SubscribeCore(IObserver<UniRx.FrameInterval<T>> observer, IDisposable cancel)
{
return source.Subscribe(new FrameInterval(observer, cancel));
}
class FrameInterval : OperatorObserverBase<T, UniRx.FrameInterval<T>>
{
int lastFrame;
public FrameInterval(IObserver<UniRx.FrameInterval<T>> observer, IDisposable cancel)
: base(observer, cancel)
{
this.lastFrame = UnityEngine.Time.frameCount;
}
public override void OnNext(T value)
{
var now = UnityEngine.Time.frameCount;
var span = now - lastFrame;
lastFrame = now;
base.observer.OnNext(new UniRx.FrameInterval<T>(value, span));
}
public override void OnError(Exception error)
{
try { observer.OnError(error); }
finally { Dispose(); }
}
public override void OnCompleted()
{
try { observer.OnCompleted(); }
finally { Dispose(); }
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a731a1a74be20a04a9d7dedc5ceefab2
timeCreated: 1467771656
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
using System;
namespace UniRx.Operators
{
internal class FrameTimeIntervalObservable<T> : OperatorObservableBase<UniRx.TimeInterval<T>>
{
readonly IObservable<T> source;
readonly bool ignoreTimeScale;
public FrameTimeIntervalObservable(IObservable<T> source, bool ignoreTimeScale)
: base(source.IsRequiredSubscribeOnCurrentThread())
{
this.source = source;
this.ignoreTimeScale = ignoreTimeScale;
}
protected override IDisposable SubscribeCore(IObserver<UniRx.TimeInterval<T>> observer, IDisposable cancel)
{
return source.Subscribe(new FrameTimeInterval(this, observer, cancel));
}
class FrameTimeInterval : OperatorObserverBase<T, UniRx.TimeInterval<T>>
{
readonly FrameTimeIntervalObservable<T> parent;
float lastTime;
public FrameTimeInterval(FrameTimeIntervalObservable<T> parent, IObserver<UniRx.TimeInterval<T>> observer, IDisposable cancel)
: base(observer, cancel)
{
this.parent = parent;
this.lastTime = (parent.ignoreTimeScale)
? UnityEngine.Time.unscaledTime
: UnityEngine.Time.time;
}
public override void OnNext(T value)
{
var now = (parent.ignoreTimeScale)
? UnityEngine.Time.unscaledTime
: UnityEngine.Time.time;
var span = now - lastTime;
lastTime = now;
base.observer.OnNext(new UniRx.TimeInterval<T>(value, TimeSpan.FromSeconds(span)));
}
public override void OnError(Exception error)
{
try { observer.OnError(error); }
finally { Dispose(); }
}
public override void OnCompleted()
{
try { observer.OnCompleted(); }
finally { Dispose(); }
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a55cce9ef638364409d1227a25a32421
timeCreated: 1467771656
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,141 @@
using System;
using System.Collections;
using System.Threading;
namespace UniRx.Operators
{
internal class FromCoroutineObservable<T> : OperatorObservableBase<T>
{
readonly Func<IObserver<T>, CancellationToken, IEnumerator> coroutine;
public FromCoroutineObservable(Func<IObserver<T>, CancellationToken, IEnumerator> coroutine)
: base(false)
{
this.coroutine = coroutine;
}
protected override IDisposable SubscribeCore(IObserver<T> observer, IDisposable cancel)
{
var fromCoroutineObserver = new FromCoroutine(observer, cancel);
#if (NETFX_CORE || NET_4_6 || NET_STANDARD_2_0 || UNITY_WSA_10_0)
var moreCancel = new CancellationDisposable();
var token = moreCancel.Token;
#else
var moreCancel = new BooleanDisposable();
var token = new CancellationToken(moreCancel);
#endif
MainThreadDispatcher.SendStartCoroutine(coroutine(fromCoroutineObserver, token));
return moreCancel;
}
class FromCoroutine : OperatorObserverBase<T, T>
{
public FromCoroutine(IObserver<T> observer, IDisposable cancel) : base(observer, cancel)
{
}
public override void OnNext(T value)
{
try
{
base.observer.OnNext(value);
}
catch
{
Dispose();
throw;
}
}
public override void OnError(Exception error)
{
try { observer.OnError(error); }
finally { Dispose(); }
}
public override void OnCompleted()
{
try { observer.OnCompleted(); }
finally { Dispose(); }
}
}
}
internal class FromMicroCoroutineObservable<T> : OperatorObservableBase<T>
{
readonly Func<IObserver<T>, CancellationToken, IEnumerator> coroutine;
readonly FrameCountType frameCountType;
public FromMicroCoroutineObservable(Func<IObserver<T>, CancellationToken, IEnumerator> coroutine, FrameCountType frameCountType)
: base(false)
{
this.coroutine = coroutine;
this.frameCountType = frameCountType;
}
protected override IDisposable SubscribeCore(IObserver<T> observer, IDisposable cancel)
{
var microCoroutineObserver = new FromMicroCoroutine(observer, cancel);
#if (NETFX_CORE || NET_4_6 || NET_STANDARD_2_0 || UNITY_WSA_10_0)
var moreCancel = new CancellationDisposable();
var token = moreCancel.Token;
#else
var moreCancel = new BooleanDisposable();
var token = new CancellationToken(moreCancel);
#endif
switch (frameCountType)
{
case FrameCountType.Update:
MainThreadDispatcher.StartUpdateMicroCoroutine(coroutine(microCoroutineObserver, token));
break;
case FrameCountType.FixedUpdate:
MainThreadDispatcher.StartFixedUpdateMicroCoroutine(coroutine(microCoroutineObserver, token));
break;
case FrameCountType.EndOfFrame:
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(coroutine(microCoroutineObserver, token));
break;
default:
throw new ArgumentException("Invalid FrameCountType:" + frameCountType);
}
return moreCancel;
}
class FromMicroCoroutine : OperatorObserverBase<T, T>
{
public FromMicroCoroutine(IObserver<T> observer, IDisposable cancel) : base(observer, cancel)
{
}
public override void OnNext(T value)
{
try
{
base.observer.OnNext(value);
}
catch
{
Dispose();
throw;
}
}
public override void OnError(Exception error)
{
try { observer.OnError(error); }
finally { Dispose(); }
}
public override void OnCompleted()
{
try { observer.OnCompleted(); }
finally { Dispose(); }
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e83ddad992535fb4f8a68a1e7ef8be60
timeCreated: 1455373901
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,178 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UniRx.Operators
{
internal class RepeatUntilObservable<T> : OperatorObservableBase<T>
{
readonly IEnumerable<IObservable<T>> sources;
readonly IObservable<Unit> trigger;
readonly GameObject lifeTimeChecker;
public RepeatUntilObservable(IEnumerable<IObservable<T>> sources, IObservable<Unit> trigger, GameObject lifeTimeChecker)
: base(true)
{
this.sources = sources;
this.trigger = trigger;
this.lifeTimeChecker = lifeTimeChecker;
}
protected override IDisposable SubscribeCore(IObserver<T> observer, IDisposable cancel)
{
return new RepeatUntil(this, observer, cancel).Run();
}
class RepeatUntil : OperatorObserverBase<T, T>
{
readonly RepeatUntilObservable<T> parent;
readonly object gate = new object();
IEnumerator<IObservable<T>> e;
SerialDisposable subscription;
SingleAssignmentDisposable schedule;
Action nextSelf;
bool isStopped;
bool isDisposed;
bool isFirstSubscribe;
IDisposable stopper;
public RepeatUntil(RepeatUntilObservable<T> parent, IObserver<T> observer, IDisposable cancel) : base(observer, cancel)
{
this.parent = parent;
}
public IDisposable Run()
{
isFirstSubscribe = true;
isDisposed = false;
isStopped = false;
e = parent.sources.GetEnumerator();
subscription = new SerialDisposable();
schedule = new SingleAssignmentDisposable();
stopper = parent.trigger.Subscribe(_ =>
{
lock (gate)
{
isStopped = true;
e.Dispose();
subscription.Dispose();
schedule.Dispose();
observer.OnCompleted();
}
}, observer.OnError);
schedule.Disposable = Scheduler.CurrentThread.Schedule(RecursiveRun);
return new CompositeDisposable(schedule, subscription, stopper, Disposable.Create(() =>
{
lock (gate)
{
isDisposed = true;
e.Dispose();
}
}));
}
void RecursiveRun(Action self)
{
lock (gate)
{
this.nextSelf = self;
if (isDisposed) return;
if (isStopped) return;
var current = default(IObservable<T>);
var hasNext = false;
var ex = default(Exception);
try
{
hasNext = e.MoveNext();
if (hasNext)
{
current = e.Current;
if (current == null) throw new InvalidOperationException("sequence is null.");
}
else
{
e.Dispose();
}
}
catch (Exception exception)
{
ex = exception;
e.Dispose();
}
if (ex != null)
{
stopper.Dispose();
observer.OnError(ex);
return;
}
if (!hasNext)
{
stopper.Dispose();
observer.OnCompleted();
return;
}
var source = e.Current;
var d = new SingleAssignmentDisposable();
subscription.Disposable = d;
if (isFirstSubscribe)
{
isFirstSubscribe = false;
d.Disposable = source.Subscribe(this);
}
else
{
MainThreadDispatcher.SendStartCoroutine(SubscribeAfterEndOfFrame(d, source, this, parent.lifeTimeChecker));
}
}
}
static IEnumerator SubscribeAfterEndOfFrame(SingleAssignmentDisposable d, IObservable<T> source, IObserver<T> observer, GameObject lifeTimeChecker)
{
yield return YieldInstructionCache.WaitForEndOfFrame;
if (!d.IsDisposed && lifeTimeChecker != null)
{
d.Disposable = source.Subscribe(observer);
}
}
public override void OnNext(T value)
{
base.observer.OnNext(value);
}
public override void OnError(Exception error)
{
try { observer.OnError(error); }
finally { Dispose(); }
}
public override void OnCompleted()
{
if (!isDisposed)
{
this.nextSelf();
}
else
{
e.Dispose();
if (!isDisposed)
{
try { observer.OnCompleted(); }
finally { Dispose(); }
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 93507e8a72a71094f870c8dbe1e5bed8
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,132 @@
using System;
#if UniRxLibrary
using UnityObservable = UniRx.ObservableUnity;
#else
using UnityObservable = UniRx.Observable;
#endif
namespace UniRx.Operators
{
internal class SampleFrameObservable<T> : OperatorObservableBase<T>
{
readonly IObservable<T> source;
readonly int frameCount;
readonly FrameCountType frameCountType;
public SampleFrameObservable(IObservable<T> source, int frameCount, FrameCountType frameCountType) : base(source.IsRequiredSubscribeOnCurrentThread())
{
this.source = source;
this.frameCount = frameCount;
this.frameCountType = frameCountType;
}
protected override IDisposable SubscribeCore(IObserver<T> observer, IDisposable cancel)
{
return new SampleFrame(this, observer, cancel).Run();
}
class SampleFrame : OperatorObserverBase<T, T>
{
readonly SampleFrameObservable<T> parent;
readonly object gate = new object();
T latestValue = default(T);
bool isUpdated = false;
bool isCompleted = false;
SingleAssignmentDisposable sourceSubscription;
public SampleFrame(SampleFrameObservable<T> parent, IObserver<T> observer, IDisposable cancel) : base(observer, cancel)
{
this.parent = parent;
}
public IDisposable Run()
{
sourceSubscription = new SingleAssignmentDisposable();
sourceSubscription.Disposable = parent.source.Subscribe(this);
var scheduling = UnityObservable.IntervalFrame(parent.frameCount, parent.frameCountType)
.Subscribe(new SampleFrameTick(this));
return StableCompositeDisposable.Create(sourceSubscription, scheduling);
}
void OnNextTick(long _)
{
lock (gate)
{
if (isUpdated)
{
var value = latestValue;
isUpdated = false;
observer.OnNext(value);
}
if (isCompleted)
{
try { observer.OnCompleted(); } finally { Dispose(); }
}
}
}
public override void OnNext(T value)
{
lock (gate)
{
latestValue = value;
isUpdated = true;
}
}
public override void OnError(Exception error)
{
lock (gate)
{
try { base.observer.OnError(error); } finally { Dispose(); }
}
}
public override void OnCompleted()
{
lock (gate)
{
isCompleted = true;
sourceSubscription.Dispose();
}
}
class SampleFrameTick : IObserver<long>
{
readonly SampleFrame parent;
public SampleFrameTick(SampleFrame parent)
{
this.parent = parent;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(long _)
{
lock (parent.gate)
{
if (parent.isUpdated)
{
var value = parent.latestValue;
parent.isUpdated = false;
parent.observer.OnNext(value);
}
if (parent.isCompleted)
{
try { parent.observer.OnCompleted(); } finally { parent.Dispose(); }
}
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e04c7fc1929a3db458bf7ae31bcd9e55
timeCreated: 1455373901
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
using System;
namespace UniRx.Operators
{
internal class SubscribeOnMainThreadObservable<T> : OperatorObservableBase<T>
{
readonly IObservable<T> source;
readonly IObservable<long> subscribeTrigger;
public SubscribeOnMainThreadObservable(IObservable<T> source, IObservable<long> subscribeTrigger)
: base(source.IsRequiredSubscribeOnCurrentThread())
{
this.source = source;
this.subscribeTrigger = subscribeTrigger;
}
protected override IDisposable SubscribeCore(IObserver<T> observer, IDisposable cancel)
{
var m = new SingleAssignmentDisposable();
var d = new SerialDisposable();
d.Disposable = m;
m.Disposable = subscribeTrigger.SubscribeWithState3(observer, d, source, (_, o, disp, s) =>
{
disp.Disposable = s.Subscribe(o);
});
return d;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: da3fd97518766ab43827991b7b5d4270
timeCreated: 1455373901
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,123 @@
using System;
#if UniRxLibrary
using UnityObservable = UniRx.ObservableUnity;
#else
using UnityObservable = UniRx.Observable;
#endif
namespace UniRx.Operators
{
internal class ThrottleFirstFrameObservable<T> : OperatorObservableBase<T>
{
readonly IObservable<T> source;
readonly int frameCount;
readonly FrameCountType frameCountType;
public ThrottleFirstFrameObservable(IObservable<T> source, int frameCount, FrameCountType frameCountType) : base(source.IsRequiredSubscribeOnCurrentThread())
{
this.source = source;
this.frameCount = frameCount;
this.frameCountType = frameCountType;
}
protected override IDisposable SubscribeCore(IObserver<T> observer, IDisposable cancel)
{
return new ThrottleFirstFrame(this, observer, cancel).Run();
}
class ThrottleFirstFrame : OperatorObserverBase<T, T>
{
readonly ThrottleFirstFrameObservable<T> parent;
readonly object gate = new object();
bool open = true;
SerialDisposable cancelable;
ThrottleFirstFrameTick tick;
public ThrottleFirstFrame(ThrottleFirstFrameObservable<T> parent, IObserver<T> observer, IDisposable cancel) : base(observer, cancel)
{
this.parent = parent;
}
public IDisposable Run()
{
tick = new ThrottleFirstFrameTick(this);
cancelable = new SerialDisposable();
var subscription = parent.source.Subscribe(this);
return StableCompositeDisposable.Create(cancelable, subscription);
}
void OnNext()
{
lock (gate)
{
open = true;
}
}
public override void OnNext(T value)
{
lock (gate)
{
if (!open) return;
observer.OnNext(value);
open = false;
}
var d = new SingleAssignmentDisposable();
cancelable.Disposable = d;
d.Disposable = UnityObservable.TimerFrame(parent.frameCount, parent.frameCountType)
.Subscribe(tick);
}
public override void OnError(Exception error)
{
cancelable.Dispose();
lock (gate)
{
try { observer.OnError(error); } finally { Dispose(); }
}
}
public override void OnCompleted()
{
cancelable.Dispose();
lock (gate)
{
try { observer.OnCompleted(); } finally { Dispose(); }
}
}
// immutable, can share.
class ThrottleFirstFrameTick : IObserver<long>
{
readonly ThrottleFirstFrame parent;
public ThrottleFirstFrameTick(ThrottleFirstFrame parent)
{
this.parent = parent;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(long _)
{
lock (parent.gate)
{
parent.open = true;
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3ec92e777b0b4d949967b0663ce8bee8
timeCreated: 1455373898
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,129 @@
using System;
#if UniRxLibrary
using UnityObservable = UniRx.ObservableUnity;
#else
using UnityObservable = UniRx.Observable;
#endif
namespace UniRx.Operators
{
internal class ThrottleFrameObservable<T> : OperatorObservableBase<T>
{
readonly IObservable<T> source;
readonly int frameCount;
readonly FrameCountType frameCountType;
public ThrottleFrameObservable(IObservable<T> source, int frameCount, FrameCountType frameCountType) : base(source.IsRequiredSubscribeOnCurrentThread())
{
this.source = source;
this.frameCount = frameCount;
this.frameCountType = frameCountType;
}
protected override IDisposable SubscribeCore(IObserver<T> observer, IDisposable cancel)
{
return new ThrottleFrame(this, observer, cancel).Run();
}
class ThrottleFrame : OperatorObserverBase<T, T>
{
readonly ThrottleFrameObservable<T> parent;
readonly object gate = new object();
T latestValue = default(T);
bool hasValue = false;
SerialDisposable cancelable;
ulong id = 0;
public ThrottleFrame(ThrottleFrameObservable<T> parent, IObserver<T> observer, IDisposable cancel) : base(observer, cancel)
{
this.parent = parent;
}
public IDisposable Run()
{
cancelable = new SerialDisposable();
var subscription = parent.source.Subscribe(this);
return StableCompositeDisposable.Create(cancelable, subscription);
}
public override void OnNext(T value)
{
ulong currentid;
lock (gate)
{
hasValue = true;
latestValue = value;
id = unchecked(id + 1);
currentid = id;
}
var d = new SingleAssignmentDisposable();
cancelable.Disposable = d;
d.Disposable = UnityObservable.TimerFrame(parent.frameCount, parent.frameCountType)
.Subscribe(new ThrottleFrameTick(this, currentid));
}
public override void OnError(Exception error)
{
cancelable.Dispose();
lock (gate)
{
hasValue = false;
id = unchecked(id + 1);
try { observer.OnError(error); } finally { Dispose(); }
}
}
public override void OnCompleted()
{
cancelable.Dispose();
lock (gate)
{
if (hasValue)
{
observer.OnNext(latestValue);
}
hasValue = false;
id = unchecked(id + 1);
try { observer.OnCompleted(); } finally { Dispose(); }
}
}
class ThrottleFrameTick : IObserver<long>
{
readonly ThrottleFrame parent;
readonly ulong currentid;
public ThrottleFrameTick(ThrottleFrame parent, ulong currentid)
{
this.parent = parent;
this.currentid = currentid;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(long _)
{
lock (parent.gate)
{
if (parent.hasValue && parent.id == currentid)
{
parent.observer.OnNext(parent.latestValue);
}
parent.hasValue = false;
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2c4ef0bfcfe787543999c7a6cda03c07
timeCreated: 1455373898
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,142 @@
using System;
#if UniRxLibrary
using UnityObservable = UniRx.ObservableUnity;
#else
using UnityObservable = UniRx.Observable;
#endif
namespace UniRx.Operators
{
internal class TimeoutFrameObservable<T> : OperatorObservableBase<T>
{
readonly IObservable<T> source;
readonly int frameCount;
readonly FrameCountType frameCountType;
public TimeoutFrameObservable(IObservable<T> source, int frameCount, FrameCountType frameCountType) : base(source.IsRequiredSubscribeOnCurrentThread())
{
this.source = source;
this.frameCount = frameCount;
this.frameCountType = frameCountType;
}
protected override IDisposable SubscribeCore(IObserver<T> observer, IDisposable cancel)
{
return new TimeoutFrame(this, observer, cancel).Run();
}
class TimeoutFrame : OperatorObserverBase<T, T>
{
readonly TimeoutFrameObservable<T> parent;
readonly object gate = new object();
ulong objectId = 0ul;
bool isTimeout = false;
SingleAssignmentDisposable sourceSubscription;
SerialDisposable timerSubscription;
public TimeoutFrame(TimeoutFrameObservable<T> parent, IObserver<T> observer, IDisposable cancel) : base(observer, cancel)
{
this.parent = parent;
}
public IDisposable Run()
{
sourceSubscription = new SingleAssignmentDisposable();
timerSubscription = new SerialDisposable();
timerSubscription.Disposable = RunTimer(objectId);
sourceSubscription.Disposable = parent.source.Subscribe(this);
return StableCompositeDisposable.Create(timerSubscription, sourceSubscription);
}
IDisposable RunTimer(ulong timerId)
{
return UnityObservable.TimerFrame(parent.frameCount, parent.frameCountType)
.Subscribe(new TimeoutFrameTick(this, timerId));
}
public override void OnNext(T value)
{
ulong useObjectId;
bool timeout;
lock (gate)
{
timeout = isTimeout;
objectId++;
useObjectId = objectId;
}
if (timeout) return;
timerSubscription.Disposable = Disposable.Empty; // cancel old timer
observer.OnNext(value);
timerSubscription.Disposable = RunTimer(useObjectId);
}
public override void OnError(Exception error)
{
bool timeout;
lock (gate)
{
timeout = isTimeout;
objectId++;
}
if (timeout) return;
timerSubscription.Dispose();
try { observer.OnError(error); } finally { Dispose(); }
}
public override void OnCompleted()
{
bool timeout;
lock (gate)
{
timeout = isTimeout;
objectId++;
}
if (timeout) return;
timerSubscription.Dispose();
try { observer.OnCompleted(); } finally { Dispose(); }
}
class TimeoutFrameTick : IObserver<long>
{
readonly TimeoutFrame parent;
readonly ulong timerId;
public TimeoutFrameTick(TimeoutFrame parent, ulong timerId)
{
this.parent = parent;
this.timerId = timerId;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(long _)
{
lock (parent.gate)
{
if (parent.objectId == timerId)
{
parent.isTimeout = true;
}
}
if (parent.isTimeout)
{
try { parent.observer.OnError(new TimeoutException()); } finally { parent.Dispose(); }
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c27be0a585d78a944bccd31b86ee6722
timeCreated: 1455373901
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,333 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace UniRx
{
public struct CollectionAddEvent<T> : IEquatable<CollectionAddEvent<T>>
{
public int Index { get; private set; }
public T Value { get; private set; }
public CollectionAddEvent(int index, T value)
:this()
{
Index = index;
Value = value;
}
public override string ToString()
{
return string.Format("Index:{0} Value:{1}", Index, Value);
}
public override int GetHashCode()
{
return Index.GetHashCode() ^ EqualityComparer<T>.Default.GetHashCode(Value) << 2;
}
public bool Equals(CollectionAddEvent<T> other)
{
return Index.Equals(other.Index) && EqualityComparer<T>.Default.Equals(Value, other.Value);
}
}
public struct CollectionRemoveEvent<T> : IEquatable<CollectionRemoveEvent<T>>
{
public int Index { get; private set; }
public T Value { get; private set; }
public CollectionRemoveEvent(int index, T value)
: this()
{
Index = index;
Value = value;
}
public override string ToString()
{
return string.Format("Index:{0} Value:{1}", Index, Value);
}
public override int GetHashCode()
{
return Index.GetHashCode() ^ EqualityComparer<T>.Default.GetHashCode(Value) << 2;
}
public bool Equals(CollectionRemoveEvent<T> other)
{
return Index.Equals(other.Index) && EqualityComparer<T>.Default.Equals(Value, other.Value);
}
}
public struct CollectionMoveEvent<T> : IEquatable<CollectionMoveEvent<T>>
{
public int OldIndex { get; private set; }
public int NewIndex { get; private set; }
public T Value { get; private set; }
public CollectionMoveEvent(int oldIndex, int newIndex, T value)
: this()
{
OldIndex = oldIndex;
NewIndex = newIndex;
Value = value;
}
public override string ToString()
{
return string.Format("OldIndex:{0} NewIndex:{1} Value:{2}", OldIndex, NewIndex, Value);
}
public override int GetHashCode()
{
return OldIndex.GetHashCode() ^ NewIndex.GetHashCode() << 2 ^ EqualityComparer<T>.Default.GetHashCode(Value) >> 2;
}
public bool Equals(CollectionMoveEvent<T> other)
{
return OldIndex.Equals(other.OldIndex) && NewIndex.Equals(other.NewIndex) && EqualityComparer<T>.Default.Equals(Value, other.Value);
}
}
public struct CollectionReplaceEvent<T> : IEquatable<CollectionReplaceEvent<T>>
{
public int Index { get; private set; }
public T OldValue { get; private set; }
public T NewValue { get; private set; }
public CollectionReplaceEvent(int index, T oldValue, T newValue)
: this()
{
Index = index;
OldValue = oldValue;
NewValue = newValue;
}
public override string ToString()
{
return string.Format("Index:{0} OldValue:{1} NewValue:{2}", Index, OldValue, NewValue);
}
public override int GetHashCode()
{
return Index.GetHashCode() ^ EqualityComparer<T>.Default.GetHashCode(OldValue) << 2 ^ EqualityComparer<T>.Default.GetHashCode(NewValue) >> 2;
}
public bool Equals(CollectionReplaceEvent<T> other)
{
return Index.Equals(other.Index)
&& EqualityComparer<T>.Default.Equals(OldValue, other.OldValue)
&& EqualityComparer<T>.Default.Equals(NewValue, other.NewValue);
}
}
// IReadOnlyList<out T> is from .NET 4.5
public interface IReadOnlyReactiveCollection<T> : IEnumerable<T>
{
int Count { get; }
T this[int index] { get; }
IObservable<CollectionAddEvent<T>> ObserveAdd();
IObservable<int> ObserveCountChanged(bool notifyCurrentCount = false);
IObservable<CollectionMoveEvent<T>> ObserveMove();
IObservable<CollectionRemoveEvent<T>> ObserveRemove();
IObservable<CollectionReplaceEvent<T>> ObserveReplace();
IObservable<Unit> ObserveReset();
}
public interface IReactiveCollection<T> : IList<T>, IReadOnlyReactiveCollection<T>
{
new int Count { get; }
new T this[int index] { get; set; }
void Move(int oldIndex, int newIndex);
}
[Serializable]
public class ReactiveCollection<T> : Collection<T>, IReactiveCollection<T>, IDisposable
{
[NonSerialized]
bool isDisposed = false;
public ReactiveCollection()
{
}
public ReactiveCollection(IEnumerable<T> collection)
{
if (collection == null) throw new ArgumentNullException("collection");
foreach (var item in collection)
{
Add(item);
}
}
public ReactiveCollection(List<T> list)
: base(list != null ? new List<T>(list) : null)
{
}
protected override void ClearItems()
{
var beforeCount = Count;
base.ClearItems();
if (collectionReset != null) collectionReset.OnNext(Unit.Default);
if (beforeCount > 0)
{
if (countChanged != null) countChanged.OnNext(Count);
}
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
if (collectionAdd != null) collectionAdd.OnNext(new CollectionAddEvent<T>(index, item));
if (countChanged != null) countChanged.OnNext(Count);
}
public void Move(int oldIndex, int newIndex)
{
MoveItem(oldIndex, newIndex);
}
protected virtual void MoveItem(int oldIndex, int newIndex)
{
T item = this[oldIndex];
base.RemoveItem(oldIndex);
base.InsertItem(newIndex, item);
if (collectionMove != null) collectionMove.OnNext(new CollectionMoveEvent<T>(oldIndex, newIndex, item));
}
protected override void RemoveItem(int index)
{
T item = this[index];
base.RemoveItem(index);
if (collectionRemove != null) collectionRemove.OnNext(new CollectionRemoveEvent<T>(index, item));
if (countChanged != null) countChanged.OnNext(Count);
}
protected override void SetItem(int index, T item)
{
T oldItem = this[index];
base.SetItem(index, item);
if (collectionReplace != null) collectionReplace.OnNext(new CollectionReplaceEvent<T>(index, oldItem, item));
}
[NonSerialized]
Subject<int> countChanged = null;
public IObservable<int> ObserveCountChanged(bool notifyCurrentCount = false)
{
if (isDisposed) return Observable.Empty<int>();
var subject = countChanged ?? (countChanged = new Subject<int>());
if (notifyCurrentCount)
{
return subject.StartWith(() => this.Count);
}
else
{
return subject;
}
}
[NonSerialized]
Subject<Unit> collectionReset = null;
public IObservable<Unit> ObserveReset()
{
if (isDisposed) return Observable.Empty<Unit>();
return collectionReset ?? (collectionReset = new Subject<Unit>());
}
[NonSerialized]
Subject<CollectionAddEvent<T>> collectionAdd = null;
public IObservable<CollectionAddEvent<T>> ObserveAdd()
{
if (isDisposed) return Observable.Empty<CollectionAddEvent<T>>();
return collectionAdd ?? (collectionAdd = new Subject<CollectionAddEvent<T>>());
}
[NonSerialized]
Subject<CollectionMoveEvent<T>> collectionMove = null;
public IObservable<CollectionMoveEvent<T>> ObserveMove()
{
if (isDisposed) return Observable.Empty<CollectionMoveEvent<T>>();
return collectionMove ?? (collectionMove = new Subject<CollectionMoveEvent<T>>());
}
[NonSerialized]
Subject<CollectionRemoveEvent<T>> collectionRemove = null;
public IObservable<CollectionRemoveEvent<T>> ObserveRemove()
{
if (isDisposed) return Observable.Empty<CollectionRemoveEvent<T>>();
return collectionRemove ?? (collectionRemove = new Subject<CollectionRemoveEvent<T>>());
}
[NonSerialized]
Subject<CollectionReplaceEvent<T>> collectionReplace = null;
public IObservable<CollectionReplaceEvent<T>> ObserveReplace()
{
if (isDisposed) return Observable.Empty<CollectionReplaceEvent<T>>();
return collectionReplace ?? (collectionReplace = new Subject<CollectionReplaceEvent<T>>());
}
void DisposeSubject<TSubject>(ref Subject<TSubject> subject)
{
if (subject != null)
{
try
{
subject.OnCompleted();
}
finally
{
subject.Dispose();
subject = null;
}
}
}
#region IDisposable Support
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
DisposeSubject(ref collectionReset);
DisposeSubject(ref collectionAdd);
DisposeSubject(ref collectionMove);
DisposeSubject(ref collectionRemove);
DisposeSubject(ref collectionReplace);
DisposeSubject(ref countChanged);
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
}
public static partial class ReactiveCollectionExtensions
{
public static ReactiveCollection<T> ToReactiveCollection<T>(this IEnumerable<T> source)
{
return new ReactiveCollection<T>(source);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2e22185fb1dbcef42bc613efd4769011
timeCreated: 1455373898
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,487 @@
using System;
using System.Collections.Generic;
using System.Threading;
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
using System.Threading.Tasks;
using UniRx.InternalUtil;
#endif
namespace UniRx
{
public interface IReactiveCommand<T> : IObservable<T>
{
IReadOnlyReactiveProperty<bool> CanExecute { get; }
bool Execute(T parameter);
}
public interface IAsyncReactiveCommand<T>
{
IReadOnlyReactiveProperty<bool> CanExecute { get; }
IDisposable Execute(T parameter);
IDisposable Subscribe(Func<T, IObservable<Unit>> asyncAction);
}
/// <summary>
/// Represents ReactiveCommand&lt;Unit&gt;
/// </summary>
public class ReactiveCommand : ReactiveCommand<Unit>
{
/// <summary>
/// CanExecute is always true.
/// </summary>
public ReactiveCommand()
: base()
{ }
/// <summary>
/// CanExecute is changed from canExecute sequence.
/// </summary>
public ReactiveCommand(IObservable<bool> canExecuteSource, bool initialValue = true)
: base(canExecuteSource, initialValue)
{
}
/// <summary>Push null to subscribers.</summary>
public bool Execute()
{
return Execute(Unit.Default);
}
/// <summary>Force push parameter to subscribers.</summary>
public void ForceExecute()
{
ForceExecute(Unit.Default);
}
}
public class ReactiveCommand<T> : IReactiveCommand<T>, IDisposable
{
readonly Subject<T> trigger = new Subject<T>();
readonly IDisposable canExecuteSubscription;
ReactiveProperty<bool> canExecute;
public IReadOnlyReactiveProperty<bool> CanExecute
{
get
{
return canExecute;
}
}
public bool IsDisposed { get; private set; }
/// <summary>
/// CanExecute is always true.
/// </summary>
public ReactiveCommand()
{
this.canExecute = new ReactiveProperty<bool>(true);
this.canExecuteSubscription = Disposable.Empty;
}
/// <summary>
/// CanExecute is changed from canExecute sequence.
/// </summary>
public ReactiveCommand(IObservable<bool> canExecuteSource, bool initialValue = true)
{
this.canExecute = new ReactiveProperty<bool>(initialValue);
this.canExecuteSubscription = canExecuteSource
.DistinctUntilChanged()
.SubscribeWithState(canExecute, (b, c) => c.Value = b);
}
/// <summary>Push parameter to subscribers when CanExecute.</summary>
public bool Execute(T parameter)
{
if (canExecute.Value)
{
trigger.OnNext(parameter);
return true;
}
else
{
return false;
}
}
/// <summary>Force push parameter to subscribers.</summary>
public void ForceExecute(T parameter)
{
trigger.OnNext(parameter);
}
/// <summary>Subscribe execute.</summary>
public IDisposable Subscribe(IObserver<T> observer)
{
return trigger.Subscribe(observer);
}
/// <summary>
/// Stop all subscription and lock CanExecute is false.
/// </summary>
public void Dispose()
{
if (IsDisposed) return;
IsDisposed = true;
canExecute.Dispose();
trigger.OnCompleted();
trigger.Dispose();
canExecuteSubscription.Dispose();
}
}
/// <summary>
/// Variation of ReactiveCommand, when executing command then CanExecute = false after CanExecute = true.
/// </summary>
public class AsyncReactiveCommand : AsyncReactiveCommand<Unit>
{
/// <summary>
/// CanExecute is automatically changed when executing to false and finished to true.
/// </summary>
public AsyncReactiveCommand()
: base()
{
}
/// <summary>
/// CanExecute is automatically changed when executing to false and finished to true.
/// </summary>
public AsyncReactiveCommand(IObservable<bool> canExecuteSource)
: base(canExecuteSource)
{
}
/// <summary>
/// CanExecute is automatically changed when executing to false and finished to true.
/// The source is shared between other AsyncReactiveCommand.
/// </summary>
public AsyncReactiveCommand(IReactiveProperty<bool> sharedCanExecute)
: base(sharedCanExecute)
{
}
public IDisposable Execute()
{
return base.Execute(Unit.Default);
}
}
/// <summary>
/// Variation of ReactiveCommand, canExecute is changed when executing command then CanExecute = false after CanExecute = true.
/// </summary>
public class AsyncReactiveCommand<T> : IAsyncReactiveCommand<T>
{
UniRx.InternalUtil.ImmutableList<Func<T, IObservable<Unit>>> asyncActions = UniRx.InternalUtil.ImmutableList<Func<T, IObservable<Unit>>>.Empty;
readonly object gate = new object();
readonly IReactiveProperty<bool> canExecuteSource;
readonly IReadOnlyReactiveProperty<bool> canExecute;
public IReadOnlyReactiveProperty<bool> CanExecute
{
get
{
return canExecute;
}
}
public bool IsDisposed { get; private set; }
/// <summary>
/// CanExecute is automatically changed when executing to false and finished to true.
/// </summary>
public AsyncReactiveCommand()
{
this.canExecuteSource = new ReactiveProperty<bool>(true);
this.canExecute = canExecuteSource;
}
/// <summary>
/// CanExecute is automatically changed when executing to false and finished to true.
/// </summary>
public AsyncReactiveCommand(IObservable<bool> canExecuteSource)
{
this.canExecuteSource = new ReactiveProperty<bool>(true);
this.canExecute = this.canExecuteSource.CombineLatest(canExecuteSource, (x, y) => x && y).ToReactiveProperty();
}
/// <summary>
/// CanExecute is automatically changed when executing to false and finished to true.
/// The source is shared between other AsyncReactiveCommand.
/// </summary>
public AsyncReactiveCommand(IReactiveProperty<bool> sharedCanExecute)
{
this.canExecuteSource = sharedCanExecute;
this.canExecute = sharedCanExecute;
}
/// <summary>Push parameter to subscribers when CanExecute.</summary>
public IDisposable Execute(T parameter)
{
if (canExecute.Value)
{
canExecuteSource.Value = false;
var a = asyncActions.Data;
if (a.Length == 1)
{
try
{
var asyncState = a[0].Invoke(parameter) ?? Observable.ReturnUnit();
return asyncState.Finally(() => canExecuteSource.Value = true).Subscribe();
}
catch
{
canExecuteSource.Value = true;
throw;
}
}
else
{
var xs = new IObservable<Unit>[a.Length];
try
{
for (int i = 0; i < a.Length; i++)
{
xs[i] = a[i].Invoke(parameter) ?? Observable.ReturnUnit();
}
}
catch
{
canExecuteSource.Value = true;
throw;
}
return Observable.WhenAll(xs).Finally(() => canExecuteSource.Value = true).Subscribe();
}
}
else
{
return Disposable.Empty;
}
}
/// <summary>Subscribe execute.</summary>
public IDisposable Subscribe(Func<T, IObservable<Unit>> asyncAction)
{
lock (gate)
{
asyncActions = asyncActions.Add(asyncAction);
}
return new Subscription(this, asyncAction);
}
/// <summary>
/// Stop all subscription and lock CanExecute is false.
/// </summary>
public void Dispose()
{
if (IsDisposed) return;
IsDisposed = true;
asyncActions = UniRx.InternalUtil.ImmutableList<Func<T, IObservable<Unit>>>.Empty;
}
class Subscription : IDisposable
{
readonly AsyncReactiveCommand<T> parent;
readonly Func<T, IObservable<Unit>> asyncAction;
public Subscription(AsyncReactiveCommand<T> parent, Func<T, IObservable<Unit>> asyncAction)
{
this.parent = parent;
this.asyncAction = asyncAction;
}
public void Dispose()
{
lock (parent.gate)
{
parent.asyncActions = parent.asyncActions.Remove(asyncAction);
}
}
}
}
public static class ReactiveCommandExtensions
{
/// <summary>
/// Create non parameter commands. CanExecute is changed from canExecute sequence.
/// </summary>
public static ReactiveCommand ToReactiveCommand(this IObservable<bool> canExecuteSource, bool initialValue = true)
{
return new ReactiveCommand(canExecuteSource, initialValue);
}
/// <summary>
/// Create parametered comamnds. CanExecute is changed from canExecute sequence.
/// </summary>
public static ReactiveCommand<T> ToReactiveCommand<T>(this IObservable<bool> canExecuteSource, bool initialValue = true)
{
return new ReactiveCommand<T>(canExecuteSource, initialValue);
}
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
static readonly Action<object> Callback = CancelCallback;
static void CancelCallback(object state)
{
var tuple = (Tuple<ICancellableTaskCompletionSource, IDisposable>)state;
tuple.Item2.Dispose();
tuple.Item1.TrySetCanceled();
}
public static Task<T> WaitUntilExecuteAsync<T>(this IReactiveCommand<T> source, CancellationToken cancellationToken = default(CancellationToken))
{
var tcs = new CancellableTaskCompletionSource<T>();
var disposable = new SingleAssignmentDisposable();
disposable.Disposable = source.Subscribe(x =>
{
disposable.Dispose(); // finish subscription.
tcs.TrySetResult(x);
}, ex => tcs.TrySetException(ex), () => tcs.TrySetCanceled());
cancellationToken.Register(Callback, Tuple.Create(tcs, disposable.Disposable), false);
return tcs.Task;
}
public static System.Runtime.CompilerServices.TaskAwaiter<T> GetAwaiter<T>(this IReactiveCommand<T> command)
{
return command.WaitUntilExecuteAsync(CancellationToken.None).GetAwaiter();
}
#endif
#if !UniRxLibrary
// for uGUI(from 4.6)
#if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5)
/// <summary>
/// Bind ReactiveCommand to button's interactable and onClick.
/// </summary>
public static IDisposable BindTo(this IReactiveCommand<Unit> command, UnityEngine.UI.Button button)
{
var d1 = command.CanExecute.SubscribeToInteractable(button);
var d2 = button.OnClickAsObservable().SubscribeWithState(command, (x, c) => c.Execute(x));
return StableCompositeDisposable.Create(d1, d2);
}
/// <summary>
/// Bind ReactiveCommand to button's interactable and onClick and register onClick action to command.
/// </summary>
public static IDisposable BindToOnClick(this IReactiveCommand<Unit> command, UnityEngine.UI.Button button, Action<Unit> onClick)
{
var d1 = command.CanExecute.SubscribeToInteractable(button);
var d2 = button.OnClickAsObservable().SubscribeWithState(command, (x, c) => c.Execute(x));
var d3 = command.Subscribe(onClick);
return StableCompositeDisposable.Create(d1, d2, d3);
}
/// <summary>
/// Bind canExecuteSource to button's interactable and onClick and register onClick action to command.
/// </summary>
public static IDisposable BindToButtonOnClick(this IObservable<bool> canExecuteSource, UnityEngine.UI.Button button, Action<Unit> onClick, bool initialValue = true)
{
return ToReactiveCommand(canExecuteSource, initialValue).BindToOnClick(button, onClick);
}
#endif
#endif
}
public static class AsyncReactiveCommandExtensions
{
public static AsyncReactiveCommand ToAsyncReactiveCommand(this IReactiveProperty<bool> sharedCanExecuteSource)
{
return new AsyncReactiveCommand(sharedCanExecuteSource);
}
public static AsyncReactiveCommand<T> ToAsyncReactiveCommand<T>(this IReactiveProperty<bool> sharedCanExecuteSource)
{
return new AsyncReactiveCommand<T>(sharedCanExecuteSource);
}
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
static readonly Action<object> Callback = CancelCallback;
static void CancelCallback(object state)
{
var tuple = (Tuple<ICancellableTaskCompletionSource, IDisposable>)state;
tuple.Item2.Dispose();
tuple.Item1.TrySetCanceled();
}
public static Task<T> WaitUntilExecuteAsync<T>(this IAsyncReactiveCommand<T> source, CancellationToken cancellationToken = default(CancellationToken))
{
var tcs = new CancellableTaskCompletionSource<T>();
var subscription = source.Subscribe(x => { tcs.TrySetResult(x); return Observable.ReturnUnit(); });
cancellationToken.Register(Callback, Tuple.Create(tcs, subscription), false);
return tcs.Task;
}
public static System.Runtime.CompilerServices.TaskAwaiter<T> GetAwaiter<T>(this IAsyncReactiveCommand<T> command)
{
return command.WaitUntilExecuteAsync(CancellationToken.None).GetAwaiter();
}
#endif
#if !UniRxLibrary
// for uGUI(from 4.6)
#if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5)
/// <summary>
/// Bind AsyncRaectiveCommand to button's interactable and onClick.
/// </summary>
public static IDisposable BindTo(this IAsyncReactiveCommand<Unit> command, UnityEngine.UI.Button button)
{
var d1 = command.CanExecute.SubscribeToInteractable(button);
var d2 = button.OnClickAsObservable().SubscribeWithState(command, (x, c) => c.Execute(x));
return StableCompositeDisposable.Create(d1, d2);
}
/// <summary>
/// Bind AsyncRaectiveCommand to button's interactable and onClick and register async action to command.
/// </summary>
public static IDisposable BindToOnClick(this IAsyncReactiveCommand<Unit> command, UnityEngine.UI.Button button, Func<Unit, IObservable<Unit>> asyncOnClick)
{
var d1 = command.CanExecute.SubscribeToInteractable(button);
var d2 = button.OnClickAsObservable().SubscribeWithState(command, (x, c) => c.Execute(x));
var d3 = command.Subscribe(asyncOnClick);
return StableCompositeDisposable.Create(d1, d2, d3);
}
/// <summary>
/// Create AsyncReactiveCommand and bind to button's interactable and onClick and register async action to command.
/// </summary>
public static IDisposable BindToOnClick(this UnityEngine.UI.Button button, Func<Unit, IObservable<Unit>> asyncOnClick)
{
return new AsyncReactiveCommand().BindToOnClick(button, asyncOnClick);
}
/// <summary>
/// Create AsyncReactiveCommand and bind sharedCanExecuteSource source to button's interactable and onClick and register async action to command.
/// </summary>
public static IDisposable BindToOnClick(this UnityEngine.UI.Button button, IReactiveProperty<bool> sharedCanExecuteSource, Func<Unit, IObservable<Unit>> asyncOnClick)
{
return sharedCanExecuteSource.ToAsyncReactiveCommand().BindToOnClick(button, asyncOnClick);
}
#endif
#endif
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 939b249fde5252f45a4404e7648931ed
timeCreated: 1462927720
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,520 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace UniRx
{
public struct DictionaryAddEvent<TKey, TValue> : IEquatable<DictionaryAddEvent<TKey, TValue>>
{
public TKey Key { get; private set; }
public TValue Value { get; private set; }
public DictionaryAddEvent(TKey key, TValue value)
: this()
{
Key = key;
Value = value;
}
public override string ToString()
{
return string.Format("Key:{0} Value:{1}", Key, Value);
}
public override int GetHashCode()
{
return EqualityComparer<TKey>.Default.GetHashCode(Key) ^ EqualityComparer<TValue>.Default.GetHashCode(Value) << 2;
}
public bool Equals(DictionaryAddEvent<TKey, TValue> other)
{
return EqualityComparer<TKey>.Default.Equals(Key, other.Key) && EqualityComparer<TValue>.Default.Equals(Value, other.Value);
}
}
public struct DictionaryRemoveEvent<TKey, TValue> : IEquatable<DictionaryRemoveEvent<TKey, TValue>>
{
public TKey Key { get; private set; }
public TValue Value { get; private set; }
public DictionaryRemoveEvent(TKey key, TValue value)
: this()
{
Key = key;
Value = value;
}
public override string ToString()
{
return string.Format("Key:{0} Value:{1}", Key, Value);
}
public override int GetHashCode()
{
return EqualityComparer<TKey>.Default.GetHashCode(Key) ^ EqualityComparer<TValue>.Default.GetHashCode(Value) << 2;
}
public bool Equals(DictionaryRemoveEvent<TKey, TValue> other)
{
return EqualityComparer<TKey>.Default.Equals(Key, other.Key) && EqualityComparer<TValue>.Default.Equals(Value, other.Value);
}
}
public struct DictionaryReplaceEvent<TKey, TValue> : IEquatable<DictionaryReplaceEvent<TKey, TValue>>
{
public TKey Key { get; private set; }
public TValue OldValue { get; private set; }
public TValue NewValue { get; private set; }
public DictionaryReplaceEvent(TKey key, TValue oldValue, TValue newValue)
: this()
{
Key = key;
OldValue = oldValue;
NewValue = newValue;
}
public override string ToString()
{
return string.Format("Key:{0} OldValue:{1} NewValue:{2}", Key, OldValue, NewValue);
}
public override int GetHashCode()
{
return EqualityComparer<TKey>.Default.GetHashCode(Key) ^ EqualityComparer<TValue>.Default.GetHashCode(OldValue) << 2 ^ EqualityComparer<TValue>.Default.GetHashCode(NewValue) >> 2;
}
public bool Equals(DictionaryReplaceEvent<TKey, TValue> other)
{
return EqualityComparer<TKey>.Default.Equals(Key, other.Key) && EqualityComparer<TValue>.Default.Equals(OldValue, other.OldValue) && EqualityComparer<TValue>.Default.Equals(NewValue, other.NewValue);
}
}
// IReadOnlyDictionary is from .NET 4.5
public interface IReadOnlyReactiveDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
int Count { get; }
TValue this[TKey index] { get; }
bool ContainsKey(TKey key);
bool TryGetValue(TKey key, out TValue value);
IObservable<DictionaryAddEvent<TKey, TValue>> ObserveAdd();
IObservable<int> ObserveCountChanged(bool notifyCurrentCount = false);
IObservable<DictionaryRemoveEvent<TKey, TValue>> ObserveRemove();
IObservable<DictionaryReplaceEvent<TKey, TValue>> ObserveReplace();
IObservable<Unit> ObserveReset();
}
public interface IReactiveDictionary<TKey, TValue> : IReadOnlyReactiveDictionary<TKey, TValue>, IDictionary<TKey, TValue>
{
}
[Serializable]
public class ReactiveDictionary<TKey, TValue> : IReactiveDictionary<TKey, TValue>, IDictionary<TKey, TValue>, IEnumerable, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, IDisposable
#if !UNITY_METRO
, ISerializable, IDeserializationCallback
#endif
{
[NonSerialized]
bool isDisposed = false;
#if !UniRxLibrary
[UnityEngine.SerializeField]
#endif
readonly Dictionary<TKey, TValue> inner;
public ReactiveDictionary()
{
inner = new Dictionary<TKey, TValue>();
}
public ReactiveDictionary(IEqualityComparer<TKey> comparer)
{
inner = new Dictionary<TKey, TValue>(comparer);
}
public ReactiveDictionary(Dictionary<TKey, TValue> innerDictionary)
{
inner = innerDictionary;
}
public TValue this[TKey key]
{
get
{
return inner[key];
}
set
{
TValue oldValue;
if (TryGetValue(key, out oldValue))
{
inner[key] = value;
if (dictionaryReplace != null) dictionaryReplace.OnNext(new DictionaryReplaceEvent<TKey, TValue>(key, oldValue, value));
}
else
{
inner[key] = value;
if (dictionaryAdd != null) dictionaryAdd.OnNext(new DictionaryAddEvent<TKey, TValue>(key, value));
if (countChanged != null) countChanged.OnNext(Count);
}
}
}
public int Count
{
get
{
return inner.Count;
}
}
public Dictionary<TKey, TValue>.KeyCollection Keys
{
get
{
return inner.Keys;
}
}
public Dictionary<TKey, TValue>.ValueCollection Values
{
get
{
return inner.Values;
}
}
public void Add(TKey key, TValue value)
{
inner.Add(key, value);
if (dictionaryAdd != null) dictionaryAdd.OnNext(new DictionaryAddEvent<TKey, TValue>(key, value));
if (countChanged != null) countChanged.OnNext(Count);
}
public void Clear()
{
var beforeCount = Count;
inner.Clear();
if (collectionReset != null) collectionReset.OnNext(Unit.Default);
if (beforeCount > 0)
{
if (countChanged != null) countChanged.OnNext(Count);
}
}
public bool Remove(TKey key)
{
TValue oldValue;
if (inner.TryGetValue(key, out oldValue))
{
var isSuccessRemove = inner.Remove(key);
if (isSuccessRemove)
{
if (dictionaryRemove != null) dictionaryRemove.OnNext(new DictionaryRemoveEvent<TKey, TValue>(key, oldValue));
if (countChanged != null) countChanged.OnNext(Count);
}
return isSuccessRemove;
}
else
{
return false;
}
}
public bool ContainsKey(TKey key)
{
return inner.ContainsKey(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
return inner.TryGetValue(key, out value);
}
public Dictionary<TKey, TValue>.Enumerator GetEnumerator()
{
return inner.GetEnumerator();
}
void DisposeSubject<TSubject>(ref Subject<TSubject> subject)
{
if (subject != null)
{
try
{
subject.OnCompleted();
}
finally
{
subject.Dispose();
subject = null;
}
}
}
#region IDisposable Support
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
DisposeSubject(ref countChanged);
DisposeSubject(ref collectionReset);
DisposeSubject(ref dictionaryAdd);
DisposeSubject(ref dictionaryRemove);
DisposeSubject(ref dictionaryReplace);
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
#region Observe
[NonSerialized]
Subject<int> countChanged = null;
public IObservable<int> ObserveCountChanged(bool notifyCurrentCount = false)
{
if (isDisposed) return Observable.Empty<int>();
var subject = countChanged ?? (countChanged = new Subject<int>());
if (notifyCurrentCount)
{
return subject.StartWith(() => this.Count);
}
else
{
return subject;
}
}
[NonSerialized]
Subject<Unit> collectionReset = null;
public IObservable<Unit> ObserveReset()
{
if (isDisposed) return Observable.Empty<Unit>();
return collectionReset ?? (collectionReset = new Subject<Unit>());
}
[NonSerialized]
Subject<DictionaryAddEvent<TKey, TValue>> dictionaryAdd = null;
public IObservable<DictionaryAddEvent<TKey, TValue>> ObserveAdd()
{
if (isDisposed) return Observable.Empty<DictionaryAddEvent<TKey, TValue>>();
return dictionaryAdd ?? (dictionaryAdd = new Subject<DictionaryAddEvent<TKey, TValue>>());
}
[NonSerialized]
Subject<DictionaryRemoveEvent<TKey, TValue>> dictionaryRemove = null;
public IObservable<DictionaryRemoveEvent<TKey, TValue>> ObserveRemove()
{
if (isDisposed) return Observable.Empty<DictionaryRemoveEvent<TKey, TValue>>();
return dictionaryRemove ?? (dictionaryRemove = new Subject<DictionaryRemoveEvent<TKey, TValue>>());
}
[NonSerialized]
Subject<DictionaryReplaceEvent<TKey, TValue>> dictionaryReplace = null;
public IObservable<DictionaryReplaceEvent<TKey, TValue>> ObserveReplace()
{
if (isDisposed) return Observable.Empty<DictionaryReplaceEvent<TKey, TValue>>();
return dictionaryReplace ?? (dictionaryReplace = new Subject<DictionaryReplaceEvent<TKey, TValue>>());
}
#endregion
#region implement explicit
object IDictionary.this[object key]
{
get
{
return this[(TKey)key];
}
set
{
this[(TKey)key] = (TValue)value;
}
}
bool IDictionary.IsFixedSize
{
get
{
return ((IDictionary)inner).IsFixedSize;
}
}
bool IDictionary.IsReadOnly
{
get
{
return ((IDictionary)inner).IsReadOnly;
}
}
bool ICollection.IsSynchronized
{
get
{
return ((IDictionary)inner).IsSynchronized;
}
}
ICollection IDictionary.Keys
{
get
{
return ((IDictionary)inner).Keys;
}
}
object ICollection.SyncRoot
{
get
{
return ((IDictionary)inner).SyncRoot;
}
}
ICollection IDictionary.Values
{
get
{
return ((IDictionary)inner).Values;
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
{
get
{
return ((ICollection<KeyValuePair<TKey, TValue>>)inner).IsReadOnly;
}
}
ICollection<TKey> IDictionary<TKey, TValue>.Keys
{
get
{
return inner.Keys;
}
}
ICollection<TValue> IDictionary<TKey, TValue>.Values
{
get
{
return inner.Values;
}
}
void IDictionary.Add(object key, object value)
{
Add((TKey)key, (TValue)value);
}
bool IDictionary.Contains(object key)
{
return ((IDictionary)inner).Contains(key);
}
void ICollection.CopyTo(Array array, int index)
{
((IDictionary)inner).CopyTo(array, index);
}
#if !UNITY_METRO
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
((ISerializable)inner).GetObjectData(info, context);
}
public void OnDeserialization(object sender)
{
((IDeserializationCallback)inner).OnDeserialization(sender);
}
#endif
void IDictionary.Remove(object key)
{
Remove((TKey)key);
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
Add((TKey)item.Key, (TValue)item.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return ((ICollection<KeyValuePair<TKey, TValue>>)inner).Contains(item);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<TKey, TValue>>)inner).CopyTo(array, arrayIndex);
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return ((ICollection<KeyValuePair<TKey, TValue>>)inner).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return inner.GetEnumerator();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
TValue v;
if (TryGetValue(item.Key, out v))
{
if (EqualityComparer<TValue>.Default.Equals(v, item.Value))
{
Remove(item.Key);
return true;
}
}
return false;
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return ((IDictionary)inner).GetEnumerator();
}
#endregion
}
public static partial class ReactiveDictionaryExtensions
{
public static ReactiveDictionary<TKey, TValue> ToReactiveDictionary<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
{
return new ReactiveDictionary<TKey, TValue>(dictionary);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 12cd1079b0fe33f429f9f174c1f849af
timeCreated: 1455373897
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,610 @@
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#endif
using System;
using System.Collections.Generic;
using System.Threading;
using UniRx.InternalUtil;
#if !UniRxLibrary
using UnityEngine;
#endif
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
using System.Threading.Tasks;
#endif
namespace UniRx
{
public interface IReadOnlyReactiveProperty<T> : IObservable<T>
{
T Value { get; }
bool HasValue { get; }
}
public interface IReactiveProperty<T> : IReadOnlyReactiveProperty<T>
{
new T Value { get; set; }
}
internal interface IObserverLinkedList<T>
{
void UnsubscribeNode(ObserverNode<T> node);
}
internal sealed class ObserverNode<T> : IObserver<T>, IDisposable
{
readonly IObserver<T> observer;
IObserverLinkedList<T> list;
public ObserverNode<T> Previous { get; internal set; }
public ObserverNode<T> Next { get; internal set; }
public ObserverNode(IObserverLinkedList<T> list, IObserver<T> observer)
{
this.list = list;
this.observer = observer;
}
public void OnNext(T value)
{
observer.OnNext(value);
}
public void OnError(Exception error)
{
observer.OnError(error);
}
public void OnCompleted()
{
observer.OnCompleted();
}
public void Dispose()
{
var sourceList = Interlocked.Exchange(ref list, null);
if (sourceList != null)
{
sourceList.UnsubscribeNode(this);
sourceList = null;
}
}
}
/// <summary>
/// Lightweight property broker.
/// </summary>
[Serializable]
public class ReactiveProperty<T> : IReactiveProperty<T>, IDisposable, IOptimizedObservable<T>, IObserverLinkedList<T>
{
#if !UniRxLibrary
static readonly IEqualityComparer<T> defaultEqualityComparer = UnityEqualityComparer.GetDefault<T>();
#else
static readonly IEqualityComparer<T> defaultEqualityComparer = EqualityComparer<T>.Default;
#endif
#if !UniRxLibrary
[SerializeField]
#endif
T value = default(T);
[NonSerialized]
ObserverNode<T> root;
[NonSerialized]
ObserverNode<T> last;
[NonSerialized]
bool isDisposed = false;
protected virtual IEqualityComparer<T> EqualityComparer
{
get
{
return defaultEqualityComparer;
}
}
public T Value
{
get
{
return value;
}
set
{
if (!EqualityComparer.Equals(this.value, value))
{
SetValue(value);
if (isDisposed)
return;
RaiseOnNext(ref value);
}
}
}
// always true, allows empty constructor 'can' publish value on subscribe.
// because sometimes value is deserialized from UnityEngine.
public bool HasValue
{
get
{
return true;
}
}
public ReactiveProperty()
: this(default(T))
{
}
public ReactiveProperty(T initialValue)
{
SetValue(initialValue);
}
void RaiseOnNext(ref T value)
{
var node = root;
while (node != null)
{
node.OnNext(value);
node = node.Next;
}
}
protected virtual void SetValue(T value)
{
this.value = value;
}
public void SetValueAndForceNotify(T value)
{
SetValue(value);
if (isDisposed)
return;
RaiseOnNext(ref value);
}
public IDisposable Subscribe(IObserver<T> observer)
{
if (isDisposed)
{
observer.OnCompleted();
return Disposable.Empty;
}
// raise latest value on subscribe
observer.OnNext(value);
// subscribe node, node as subscription.
var next = new ObserverNode<T>(this, observer);
if (root == null)
{
root = last = next;
}
else
{
last.Next = next;
next.Previous = last;
last = next;
}
return next;
}
void IObserverLinkedList<T>.UnsubscribeNode(ObserverNode<T> node)
{
if (node == root)
{
root = node.Next;
}
if (node == last)
{
last = node.Previous;
}
if (node.Previous != null)
{
node.Previous.Next = node.Next;
}
if (node.Next != null)
{
node.Next.Previous = node.Previous;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (isDisposed) return;
var node = root;
root = last = null;
isDisposed = true;
while (node != null)
{
node.OnCompleted();
node = node.Next;
}
}
public override string ToString()
{
return (value == null) ? "(null)" : value.ToString();
}
public bool IsRequiredSubscribeOnCurrentThread()
{
return false;
}
}
/// <summary>
/// Lightweight property broker.
/// </summary>
public class ReadOnlyReactiveProperty<T> : IReadOnlyReactiveProperty<T>, IDisposable, IOptimizedObservable<T>, IObserverLinkedList<T>, IObserver<T>
{
#if !UniRxLibrary
static readonly IEqualityComparer<T> defaultEqualityComparer = UnityEqualityComparer.GetDefault<T>();
#else
static readonly IEqualityComparer<T> defaultEqualityComparer = EqualityComparer<T>.Default;
#endif
readonly bool distinctUntilChanged = true;
bool canPublishValueOnSubscribe = false;
bool isDisposed = false;
bool isSourceCompleted = false;
T latestValue = default(T);
Exception lastException = null;
IDisposable sourceConnection = null;
ObserverNode<T> root;
ObserverNode<T> last;
public T Value
{
get
{
return latestValue;
}
}
public bool HasValue
{
get
{
return canPublishValueOnSubscribe;
}
}
protected virtual IEqualityComparer<T> EqualityComparer
{
get
{
return defaultEqualityComparer;
}
}
public ReadOnlyReactiveProperty(IObservable<T> source)
{
this.sourceConnection = source.Subscribe(this);
}
public ReadOnlyReactiveProperty(IObservable<T> source, bool distinctUntilChanged)
{
this.distinctUntilChanged = distinctUntilChanged;
this.sourceConnection = source.Subscribe(this);
}
public ReadOnlyReactiveProperty(IObservable<T> source, T initialValue)
{
this.latestValue = initialValue;
this.canPublishValueOnSubscribe = true;
this.sourceConnection = source.Subscribe(this);
}
public ReadOnlyReactiveProperty(IObservable<T> source, T initialValue, bool distinctUntilChanged)
{
this.distinctUntilChanged = distinctUntilChanged;
this.latestValue = initialValue;
this.canPublishValueOnSubscribe = true;
this.sourceConnection = source.Subscribe(this);
}
public IDisposable Subscribe(IObserver<T> observer)
{
if (lastException != null)
{
observer.OnError(lastException);
return Disposable.Empty;
}
if (isSourceCompleted)
{
if (canPublishValueOnSubscribe)
{
observer.OnNext(latestValue);
observer.OnCompleted();
return Disposable.Empty;
}
else
{
observer.OnCompleted();
return Disposable.Empty;
}
}
if (isDisposed)
{
observer.OnCompleted();
return Disposable.Empty;
}
if (canPublishValueOnSubscribe)
{
observer.OnNext(latestValue);
}
// subscribe node, node as subscription.
var next = new ObserverNode<T>(this, observer);
if (root == null)
{
root = last = next;
}
else
{
last.Next = next;
next.Previous = last;
last = next;
}
return next;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (isDisposed) return;
sourceConnection.Dispose();
var node = root;
root = last = null;
isDisposed = true;
while (node != null)
{
node.OnCompleted();
node = node.Next;
}
}
void IObserverLinkedList<T>.UnsubscribeNode(ObserverNode<T> node)
{
if (node == root)
{
root = node.Next;
}
if (node == last)
{
last = node.Previous;
}
if (node.Previous != null)
{
node.Previous.Next = node.Next;
}
if (node.Next != null)
{
node.Next.Previous = node.Previous;
}
}
void IObserver<T>.OnNext(T value)
{
if (isDisposed) return;
if (canPublishValueOnSubscribe)
{
if (distinctUntilChanged && EqualityComparer.Equals(this.latestValue, value))
{
return;
}
}
canPublishValueOnSubscribe = true;
// SetValue
this.latestValue = value;
// call source.OnNext
var node = root;
while (node != null)
{
node.OnNext(value);
node = node.Next;
}
}
void IObserver<T>.OnError(Exception error)
{
lastException = error;
// call source.OnError
var node = root;
while (node != null)
{
node.OnError(error);
node = node.Next;
}
root = last = null;
}
void IObserver<T>.OnCompleted()
{
isSourceCompleted = true;
root = last = null;
}
public override string ToString()
{
return (latestValue == null) ? "(null)" : latestValue.ToString();
}
public bool IsRequiredSubscribeOnCurrentThread()
{
return false;
}
}
/// <summary>
/// Extension methods of ReactiveProperty&lt;T&gt;
/// </summary>
public static class ReactivePropertyExtensions
{
public static IReadOnlyReactiveProperty<T> ToReactiveProperty<T>(this IObservable<T> source)
{
return new ReadOnlyReactiveProperty<T>(source);
}
public static IReadOnlyReactiveProperty<T> ToReactiveProperty<T>(this IObservable<T> source, T initialValue)
{
return new ReadOnlyReactiveProperty<T>(source, initialValue);
}
public static ReadOnlyReactiveProperty<T> ToReadOnlyReactiveProperty<T>(this IObservable<T> source)
{
return new ReadOnlyReactiveProperty<T>(source);
}
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
static readonly Action<object> Callback = CancelCallback;
static void CancelCallback(object state)
{
var tuple = (Tuple<ICancellableTaskCompletionSource, IDisposable>)state;
tuple.Item2.Dispose();
tuple.Item1.TrySetCanceled();
}
public static Task<T> WaitUntilValueChangedAsync<T>(this IReadOnlyReactiveProperty<T> source, CancellationToken cancellationToken = default(CancellationToken))
{
var tcs = new CancellableTaskCompletionSource<T>();
var disposable = new SingleAssignmentDisposable();
if (source.HasValue)
{
// Skip first value
var isFirstValue = true;
disposable.Disposable = source.Subscribe(x =>
{
if (isFirstValue)
{
isFirstValue = false;
return;
}
else
{
disposable.Dispose(); // finish subscription.
tcs.TrySetResult(x);
}
}, ex => tcs.TrySetException(ex), () => tcs.TrySetCanceled());
}
else
{
disposable.Disposable = source.Subscribe(x =>
{
disposable.Dispose(); // finish subscription.
tcs.TrySetResult(x);
}, ex => tcs.TrySetException(ex), () => tcs.TrySetCanceled());
}
cancellationToken.Register(Callback, Tuple.Create(tcs, disposable.Disposable), false);
return tcs.Task;
}
public static System.Runtime.CompilerServices.TaskAwaiter<T> GetAwaiter<T>(this IReadOnlyReactiveProperty<T> source)
{
return source.WaitUntilValueChangedAsync(CancellationToken.None).GetAwaiter();
}
#endif
/// <summary>
/// Create ReadOnlyReactiveProperty with distinctUntilChanged: false.
/// </summary>
public static ReadOnlyReactiveProperty<T> ToSequentialReadOnlyReactiveProperty<T>(this IObservable<T> source)
{
return new ReadOnlyReactiveProperty<T>(source, distinctUntilChanged: false);
}
public static ReadOnlyReactiveProperty<T> ToReadOnlyReactiveProperty<T>(this IObservable<T> source, T initialValue)
{
return new ReadOnlyReactiveProperty<T>(source, initialValue);
}
/// <summary>
/// Create ReadOnlyReactiveProperty with distinctUntilChanged: false.
/// </summary>
public static ReadOnlyReactiveProperty<T> ToSequentialReadOnlyReactiveProperty<T>(this IObservable<T> source, T initialValue)
{
return new ReadOnlyReactiveProperty<T>(source, initialValue, distinctUntilChanged: false);
}
public static IObservable<T> SkipLatestValueOnSubscribe<T>(this IReadOnlyReactiveProperty<T> source)
{
return source.HasValue ? source.Skip(1) : source;
}
// for multiple toggle or etc..
/// <summary>
/// Lastest values of each sequence are all true.
/// </summary>
public static IObservable<bool> CombineLatestValuesAreAllTrue(this IEnumerable<IObservable<bool>> sources)
{
return sources.CombineLatest().Select(xs =>
{
foreach (var item in xs)
{
if (item == false)
return false;
}
return true;
});
}
/// <summary>
/// Lastest values of each sequence are all false.
/// </summary>
public static IObservable<bool> CombineLatestValuesAreAllFalse(this IEnumerable<IObservable<bool>> sources)
{
return sources.CombineLatest().Select(xs =>
{
foreach (var item in xs)
{
if (item == true)
return false;
}
return true;
});
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 88e12aa895fef434fbe3ea0cc8f57301
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,84 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
namespace UniRx
{
[InitializeOnLoad]
public class ScenePlaybackDetector
{
private static bool _isPlaying = false;
private static bool AboutToStartScene
{
get
{
return EditorPrefs.GetBool("AboutToStartScene");
}
set
{
EditorPrefs.SetBool("AboutToStartScene", value);
}
}
public static bool IsPlaying
{
get
{
return _isPlaying;
}
set
{
if (_isPlaying != value)
{
_isPlaying = value;
}
}
}
// This callback is notified after scripts have been reloaded.
[DidReloadScripts]
public static void OnDidReloadScripts()
{
// Filter DidReloadScripts callbacks to the moment where playmodeState transitions into isPlaying.
if (AboutToStartScene)
{
IsPlaying = true;
}
}
// InitializeOnLoad ensures that this constructor is called when the Unity Editor is started.
static ScenePlaybackDetector()
{
#if UNITY_2017_2_OR_NEWER
EditorApplication.playModeStateChanged += e =>
#else
EditorApplication.playmodeStateChanged += () =>
#endif
{
// Before scene start: isPlayingOrWillChangePlaymode = false; isPlaying = false
// Pressed Playback button: isPlayingOrWillChangePlaymode = true; isPlaying = false
// Playing: isPlayingOrWillChangePlaymode = false; isPlaying = true
// Pressed stop button: isPlayingOrWillChangePlaymode = true; isPlaying = true
if (EditorApplication.isPlayingOrWillChangePlaymode && !EditorApplication.isPlaying)
{
AboutToStartScene = true;
}
else
{
AboutToStartScene = false;
}
// Detect when playback is stopped.
if (!EditorApplication.isPlaying)
{
IsPlaying = false;
}
};
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8d380d86e2ef6674c83ca983a1604273
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 726595c7d6d85824887a77691e3ca50a
folderAsset: yes
timeCreated: 1468655394
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,474 @@
#if UNITY_5_3_OR_NEWER
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace UniRx.Toolkit
{
/// <summary>
/// Bass class of ObjectPool.
/// </summary>
public abstract class ObjectPool<T> : IDisposable
where T : UnityEngine.Component
{
bool isDisposed = false;
Queue<T> q;
/// <summary>
/// Limit of instace count.
/// </summary>
protected int MaxPoolCount
{
get
{
return int.MaxValue;
}
}
/// <summary>
/// Create instance when needed.
/// </summary>
protected abstract T CreateInstance();
/// <summary>
/// Called before return to pool, useful for set active object(it is default behavior).
/// </summary>
protected virtual void OnBeforeRent(T instance)
{
instance.gameObject.SetActive(true);
}
/// <summary>
/// Called before return to pool, useful for set inactive object(it is default behavior).
/// </summary>
protected virtual void OnBeforeReturn(T instance)
{
instance.gameObject.SetActive(false);
}
/// <summary>
/// Called when clear or disposed, useful for destroy instance or other finalize method.
/// </summary>
protected virtual void OnClear(T instance)
{
if (instance == null) return;
var go = instance.gameObject;
if (go == null) return;
UnityEngine.Object.Destroy(go);
}
/// <summary>
/// Current pooled object count.
/// </summary>
public int Count
{
get
{
if (q == null) return 0;
return q.Count;
}
}
/// <summary>
/// Get instance from pool.
/// </summary>
public T Rent()
{
if (isDisposed) throw new ObjectDisposedException("ObjectPool was already disposed.");
if (q == null) q = new Queue<T>();
var instance = (q.Count > 0)
? q.Dequeue()
: CreateInstance();
OnBeforeRent(instance);
return instance;
}
/// <summary>
/// Return instance to pool.
/// </summary>
public void Return(T instance)
{
if (isDisposed) throw new ObjectDisposedException("ObjectPool was already disposed.");
if (instance == null) throw new ArgumentNullException("instance");
if (q == null) q = new Queue<T>();
if ((q.Count + 1) == MaxPoolCount)
{
throw new InvalidOperationException("Reached Max PoolSize");
}
OnBeforeReturn(instance);
q.Enqueue(instance);
}
/// <summary>
/// Clear pool.
/// </summary>
public void Clear(bool callOnBeforeRent = false)
{
if (q == null) return;
while (q.Count != 0)
{
var instance = q.Dequeue();
if (callOnBeforeRent)
{
OnBeforeRent(instance);
}
OnClear(instance);
}
}
/// <summary>
/// Trim pool instances.
/// </summary>
/// <param name="instanceCountRatio">0.0f = clear all ~ 1.0f = live all.</param>
/// <param name="minSize">Min pool count.</param>
/// <param name="callOnBeforeRent">If true, call OnBeforeRent before OnClear.</param>
public void Shrink(float instanceCountRatio, int minSize, bool callOnBeforeRent = false)
{
if (q == null) return;
if (instanceCountRatio <= 0) instanceCountRatio = 0;
if (instanceCountRatio >= 1.0f) instanceCountRatio = 1.0f;
var size = (int)(q.Count * instanceCountRatio);
size = Math.Max(minSize, size);
while (q.Count > size)
{
var instance = q.Dequeue();
if (callOnBeforeRent)
{
OnBeforeRent(instance);
}
OnClear(instance);
}
}
/// <summary>
/// If needs shrink pool frequently, start check timer.
/// </summary>
/// <param name="checkInterval">Interval of call Shrink.</param>
/// <param name="instanceCountRatio">0.0f = clearAll ~ 1.0f = live all.</param>
/// <param name="minSize">Min pool count.</param>
/// <param name="callOnBeforeRent">If true, call OnBeforeRent before OnClear.</param>
public IDisposable StartShrinkTimer(TimeSpan checkInterval, float instanceCountRatio, int minSize, bool callOnBeforeRent = false)
{
return Observable.Interval(checkInterval)
.TakeWhile(_ => !isDisposed)
.Subscribe(_ =>
{
Shrink(instanceCountRatio, minSize, callOnBeforeRent);
});
}
/// <summary>
/// Fill pool before rent operation.
/// </summary>
/// <param name="preloadCount">Pool instance count.</param>
/// <param name="threshold">Create count per frame.</param>
public IObservable<Unit> PreloadAsync(int preloadCount, int threshold)
{
if (q == null) q = new Queue<T>(preloadCount);
return Observable.FromMicroCoroutine<Unit>((observer, cancel) => PreloadCore(preloadCount, threshold, observer, cancel));
}
IEnumerator PreloadCore(int preloadCount, int threshold, IObserver<Unit> observer, CancellationToken cancellationToken)
{
while (Count < preloadCount && !cancellationToken.IsCancellationRequested)
{
var requireCount = preloadCount - Count;
if (requireCount <= 0) break;
var createCount = Math.Min(requireCount, threshold);
for (int i = 0; i < createCount; i++)
{
try
{
var instance = CreateInstance();
Return(instance);
}
catch (Exception ex)
{
observer.OnError(ex);
yield break;
}
}
yield return null; // next frame.
}
observer.OnNext(Unit.Default);
observer.OnCompleted();
}
#region IDisposable Support
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
if (disposing)
{
Clear(false);
}
isDisposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
}
/// <summary>
/// Bass class of ObjectPool. If needs asynchronous initialization, use this instead of standard ObjectPool.
/// </summary>
public abstract class AsyncObjectPool<T> : IDisposable
where T : UnityEngine.Component
{
bool isDisposed = false;
Queue<T> q;
/// <summary>
/// Limit of instace count.
/// </summary>
protected int MaxPoolCount
{
get
{
return int.MaxValue;
}
}
/// <summary>
/// Create instance when needed.
/// </summary>
protected abstract IObservable<T> CreateInstanceAsync();
/// <summary>
/// Called before return to pool, useful for set active object(it is default behavior).
/// </summary>
protected virtual void OnBeforeRent(T instance)
{
instance.gameObject.SetActive(true);
}
/// <summary>
/// Called before return to pool, useful for set inactive object(it is default behavior).
/// </summary>
protected virtual void OnBeforeReturn(T instance)
{
instance.gameObject.SetActive(false);
}
/// <summary>
/// Called when clear or disposed, useful for destroy instance or other finalize method.
/// </summary>
protected virtual void OnClear(T instance)
{
if (instance == null) return;
var go = instance.gameObject;
if (go == null) return;
UnityEngine.Object.Destroy(go);
}
/// <summary>
/// Current pooled object count.
/// </summary>
public int Count
{
get
{
if (q == null) return 0;
return q.Count;
}
}
/// <summary>
/// Get instance from pool.
/// </summary>
public IObservable<T> RentAsync()
{
if (isDisposed) throw new ObjectDisposedException("ObjectPool was already disposed.");
if (q == null) q = new Queue<T>();
if (q.Count > 0)
{
var instance = q.Dequeue();
OnBeforeRent(instance);
return Observable.Return(instance);
}
else
{
var instance = CreateInstanceAsync();
return instance.Do(x => OnBeforeRent(x));
}
}
/// <summary>
/// Return instance to pool.
/// </summary>
public void Return(T instance)
{
if (isDisposed) throw new ObjectDisposedException("ObjectPool was already disposed.");
if (instance == null) throw new ArgumentNullException("instance");
if (q == null) q = new Queue<T>();
if ((q.Count + 1) == MaxPoolCount)
{
throw new InvalidOperationException("Reached Max PoolSize");
}
OnBeforeReturn(instance);
q.Enqueue(instance);
}
/// <summary>
/// Trim pool instances.
/// </summary>
/// <param name="instanceCountRatio">0.0f = clear all ~ 1.0f = live all.</param>
/// <param name="minSize">Min pool count.</param>
/// <param name="callOnBeforeRent">If true, call OnBeforeRent before OnClear.</param>
public void Shrink(float instanceCountRatio, int minSize, bool callOnBeforeRent = false)
{
if (q == null) return;
if (instanceCountRatio <= 0) instanceCountRatio = 0;
if (instanceCountRatio >= 1.0f) instanceCountRatio = 1.0f;
var size = (int)(q.Count * instanceCountRatio);
size = Math.Max(minSize, size);
while (q.Count > size)
{
var instance = q.Dequeue();
if (callOnBeforeRent)
{
OnBeforeRent(instance);
}
OnClear(instance);
}
}
/// <summary>
/// If needs shrink pool frequently, start check timer.
/// </summary>
/// <param name="checkInterval">Interval of call Shrink.</param>
/// <param name="instanceCountRatio">0.0f = clearAll ~ 1.0f = live all.</param>
/// <param name="minSize">Min pool count.</param>
/// <param name="callOnBeforeRent">If true, call OnBeforeRent before OnClear.</param>
public IDisposable StartShrinkTimer(TimeSpan checkInterval, float instanceCountRatio, int minSize, bool callOnBeforeRent = false)
{
return Observable.Interval(checkInterval)
.TakeWhile(_ => !isDisposed)
.Subscribe(_ =>
{
Shrink(instanceCountRatio, minSize, callOnBeforeRent);
});
}
/// <summary>
/// Clear pool.
/// </summary>
public void Clear(bool callOnBeforeRent = false)
{
if (q == null) return;
while (q.Count != 0)
{
var instance = q.Dequeue();
if (callOnBeforeRent)
{
OnBeforeRent(instance);
}
OnClear(instance);
}
}
/// <summary>
/// Fill pool before rent operation.
/// </summary>
/// <param name="preloadCount">Pool instance count.</param>
/// <param name="threshold">Create count per frame.</param>
public IObservable<Unit> PreloadAsync(int preloadCount, int threshold)
{
if (q == null) q = new Queue<T>(preloadCount);
return Observable.FromMicroCoroutine<Unit>((observer, cancel) => PreloadCore(preloadCount, threshold, observer, cancel));
}
IEnumerator PreloadCore(int preloadCount, int threshold, IObserver<Unit> observer, CancellationToken cancellationToken)
{
while (Count < preloadCount && !cancellationToken.IsCancellationRequested)
{
var requireCount = preloadCount - Count;
if (requireCount <= 0) break;
var createCount = Math.Min(requireCount, threshold);
var loaders = new IObservable<Unit>[createCount];
for (int i = 0; i < createCount; i++)
{
var instanceFuture = CreateInstanceAsync();
loaders[i] = instanceFuture.ForEachAsync(x => Return(x));
}
var awaiter = Observable.WhenAll(loaders).ToYieldInstruction(false, cancellationToken);
while (!(awaiter.HasResult || awaiter.IsCanceled || awaiter.HasError))
{
yield return null;
}
if (awaiter.HasError)
{
observer.OnError(awaiter.Error);
yield break;
}
else if (awaiter.IsCanceled)
{
yield break; // end.
}
}
observer.OnNext(Unit.Default);
observer.OnCompleted();
}
#region IDisposable Support
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
if (disposing)
{
Clear(false);
}
isDisposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f4980e1e001c7e94fab3250ba284dc91
timeCreated: 1468655394
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 5380144628ecdc74ab6778f80d103d51
folderAsset: yes
timeCreated: 1455373896
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
using System; // require keep for Windows Universal App
using UnityEngine;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableAnimatorTrigger : ObservableTriggerBase
{
Subject<int> onAnimatorIK;
/// <summary>Callback for setting up animation IK (inverse kinematics).</summary>
void OnAnimatorIK(int layerIndex)
{
if (onAnimatorIK != null) onAnimatorIK.OnNext(layerIndex);
}
/// <summary>Callback for setting up animation IK (inverse kinematics).</summary>
public IObservable<int> OnAnimatorIKAsObservable()
{
return onAnimatorIK ?? (onAnimatorIK = new Subject<int>());
}
Subject<Unit> onAnimatorMove;
/// <summary>Callback for processing animation movements for modifying root motion.</summary>
void OnAnimatorMove()
{
if (onAnimatorMove != null) onAnimatorMove.OnNext(Unit.Default);
}
/// <summary>Callback for processing animation movements for modifying root motion.</summary>
public IObservable<Unit> OnAnimatorMoveAsObservable()
{
return onAnimatorMove ?? (onAnimatorMove = new Subject<Unit>());
}
protected override void RaiseOnCompletedOnDestroy()
{
if (onAnimatorIK != null)
{
onAnimatorIK.OnCompleted();
}
if (onAnimatorMove != null)
{
onAnimatorMove.OnCompleted();
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e03f9257cc6667f4082439aa77d6f01e
timeCreated: 1455373901
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
// for uGUI(from 4.6)
#if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5)
using System; // require keep for Windows Universal App
using UnityEngine;
using UnityEngine.EventSystems;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableBeginDragTrigger : ObservableTriggerBase, IEventSystemHandler, IBeginDragHandler
{
Subject<PointerEventData> onBeginDrag;
void IBeginDragHandler.OnBeginDrag(PointerEventData eventData)
{
if (onBeginDrag != null) onBeginDrag.OnNext(eventData);
}
public IObservable<PointerEventData> OnBeginDragAsObservable()
{
return onBeginDrag ?? (onBeginDrag = new Subject<PointerEventData>());
}
protected override void RaiseOnCompletedOnDestroy()
{
if (onBeginDrag != null)
{
onBeginDrag.OnCompleted();
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3a81a9b6bec6b4f4fba7e0047cd989f6
timeCreated: 1455373898
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
// for uGUI(from 4.6)
#if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5)
using System; // require keep for Windows Universal App
using UnityEngine;
using UnityEngine.EventSystems;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableCancelTrigger : ObservableTriggerBase, IEventSystemHandler, ICancelHandler
{
Subject<BaseEventData> onCancel;
void ICancelHandler.OnCancel(BaseEventData eventData)
{
if (onCancel != null) onCancel.OnNext(eventData);
}
public IObservable<BaseEventData> OnCancelAsObservable()
{
return onCancel ?? (onCancel = new Subject<BaseEventData>());
}
protected override void RaiseOnCompletedOnDestroy()
{
if (onCancel != null)
{
onCancel.OnCompleted();
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4c0a9070b7cc23746b2c0e2db3ec16cd
timeCreated: 1455373898
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
// after uGUI(from 4.6)
#if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5)
using System;
using UnityEngine;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableCanvasGroupChangedTrigger : ObservableTriggerBase
{
Subject<Unit> onCanvasGroupChanged;
// Callback that is sent if the canvas group is changed
void OnCanvasGroupChanged()
{
if (onCanvasGroupChanged != null) onCanvasGroupChanged.OnNext(Unit.Default);
}
/// <summary>Callback that is sent if the canvas group is changed.</summary>
public IObservable<Unit> OnCanvasGroupChangedAsObservable()
{
return onCanvasGroupChanged ?? (onCanvasGroupChanged = new Subject<Unit>());
}
protected override void RaiseOnCompletedOnDestroy()
{
if (onCanvasGroupChanged != null)
{
onCanvasGroupChanged.OnCompleted();
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 54095d3e740f7714085d0568207cbfe0
timeCreated: 1455373899
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,67 @@
using System; // require keep for Windows Universal App
using UnityEngine;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableCollision2DTrigger : ObservableTriggerBase
{
Subject<Collision2D> onCollisionEnter2D;
/// <summary>Sent when an incoming collider makes contact with this object's collider (2D physics only).</summary>
void OnCollisionEnter2D(Collision2D coll)
{
if (onCollisionEnter2D != null) onCollisionEnter2D.OnNext(coll);
}
/// <summary>Sent when an incoming collider makes contact with this object's collider (2D physics only).</summary>
public IObservable<Collision2D> OnCollisionEnter2DAsObservable()
{
return onCollisionEnter2D ?? (onCollisionEnter2D = new Subject<Collision2D>());
}
Subject<Collision2D> onCollisionExit2D;
/// <summary>Sent when a collider on another object stops touching this object's collider (2D physics only).</summary>
void OnCollisionExit2D(Collision2D coll)
{
if (onCollisionExit2D != null) onCollisionExit2D.OnNext(coll);
}
/// <summary>Sent when a collider on another object stops touching this object's collider (2D physics only).</summary>
public IObservable<Collision2D> OnCollisionExit2DAsObservable()
{
return onCollisionExit2D ?? (onCollisionExit2D = new Subject<Collision2D>());
}
Subject<Collision2D> onCollisionStay2D;
/// <summary>Sent each frame where a collider on another object is touching this object's collider (2D physics only).</summary>
void OnCollisionStay2D(Collision2D coll)
{
if (onCollisionStay2D != null) onCollisionStay2D.OnNext(coll);
}
/// <summary>Sent each frame where a collider on another object is touching this object's collider (2D physics only).</summary>
public IObservable<Collision2D> OnCollisionStay2DAsObservable()
{
return onCollisionStay2D ?? (onCollisionStay2D = new Subject<Collision2D>());
}
protected override void RaiseOnCompletedOnDestroy()
{
if (onCollisionEnter2D != null)
{
onCollisionEnter2D.OnCompleted();
}
if (onCollisionExit2D != null)
{
onCollisionExit2D.OnCompleted();
}
if (onCollisionStay2D != null)
{
onCollisionStay2D.OnCompleted();
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1be7847b61f30f24daa5762db87a5b19
timeCreated: 1455373897
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,67 @@
using System; // require keep for Windows Universal App
using UnityEngine;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableCollisionTrigger : ObservableTriggerBase
{
Subject<Collision> onCollisionEnter;
/// <summary>OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider.</summary>
void OnCollisionEnter(Collision collision)
{
if (onCollisionEnter != null) onCollisionEnter.OnNext(collision);
}
/// <summary>OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider.</summary>
public IObservable<Collision> OnCollisionEnterAsObservable()
{
return onCollisionEnter ?? (onCollisionEnter = new Subject<Collision>());
}
Subject<Collision> onCollisionExit;
/// <summary>OnCollisionExit is called when this collider/rigidbody has stopped touching another rigidbody/collider.</summary>
void OnCollisionExit(Collision collisionInfo)
{
if (onCollisionExit != null) onCollisionExit.OnNext(collisionInfo);
}
/// <summary>OnCollisionExit is called when this collider/rigidbody has stopped touching another rigidbody/collider.</summary>
public IObservable<Collision> OnCollisionExitAsObservable()
{
return onCollisionExit ?? (onCollisionExit = new Subject<Collision>());
}
Subject<Collision> onCollisionStay;
/// <summary>OnCollisionStay is called once per frame for every collider/rigidbody that is touching rigidbody/collider.</summary>
void OnCollisionStay(Collision collisionInfo)
{
if (onCollisionStay != null) onCollisionStay.OnNext(collisionInfo);
}
/// <summary>OnCollisionStay is called once per frame for every collider/rigidbody that is touching rigidbody/collider.</summary>
public IObservable<Collision> OnCollisionStayAsObservable()
{
return onCollisionStay ?? (onCollisionStay = new Subject<Collision>());
}
protected override void RaiseOnCompletedOnDestroy()
{
if (onCollisionEnter != null)
{
onCollisionEnter.OnCompleted();
}
if (onCollisionExit != null)
{
onCollisionExit.OnCompleted();
}
if (onCollisionStay != null)
{
onCollisionStay.OnCompleted();
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 10b917196cbfcf74898ce1686e205d04
timeCreated: 1455373897
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
// for uGUI(from 4.6)
#if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5)
using System; // require keep for Windows Universal App
using UnityEngine;
using UnityEngine.EventSystems;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableDeselectTrigger : ObservableTriggerBase, IEventSystemHandler, IDeselectHandler
{
Subject<BaseEventData> onDeselect;
void IDeselectHandler.OnDeselect(BaseEventData eventData)
{
if (onDeselect != null) onDeselect.OnNext(eventData);
}
public IObservable<BaseEventData> OnDeselectAsObservable()
{
return onDeselect ?? (onDeselect = new Subject<BaseEventData>());
}
protected override void RaiseOnCompletedOnDestroy()
{
if (onDeselect != null)
{
onDeselect.OnCompleted();
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9fe6f69c4d869c04e8a1924aab1d3694
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,67 @@
using System; // require keep for Windows Universal App
using UnityEngine;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableDestroyTrigger : MonoBehaviour
{
bool calledDestroy = false;
Subject<Unit> onDestroy;
CompositeDisposable disposablesOnDestroy;
[Obsolete("Internal Use.")]
internal bool IsMonitoredActivate { get; set; }
public bool IsActivated { get; private set; }
/// <summary>
/// Check called OnDestroy.
/// This property does not guarantees GameObject was destroyed,
/// when gameObject is deactive, does not raise OnDestroy.
/// </summary>
public bool IsCalledOnDestroy { get { return calledDestroy; } }
void Awake()
{
IsActivated = true;
}
/// <summary>This function is called when the MonoBehaviour will be destroyed.</summary>
void OnDestroy()
{
if (!calledDestroy)
{
calledDestroy = true;
if (disposablesOnDestroy != null) disposablesOnDestroy.Dispose();
if (onDestroy != null) { onDestroy.OnNext(Unit.Default); onDestroy.OnCompleted(); }
}
}
/// <summary>This function is called when the MonoBehaviour will be destroyed.</summary>
public IObservable<Unit> OnDestroyAsObservable()
{
if (this == null) return Observable.Return(Unit.Default);
if (calledDestroy) return Observable.Return(Unit.Default);
return onDestroy ?? (onDestroy = new Subject<Unit>());
}
/// <summary>Invoke OnDestroy, this method is used on internal.</summary>
public void ForceRaiseOnDestroy()
{
OnDestroy();
}
public void AddDisposableOnDestroy(IDisposable disposable)
{
if (calledDestroy)
{
disposable.Dispose();
return;
}
if (disposablesOnDestroy == null) disposablesOnDestroy = new CompositeDisposable();
disposablesOnDestroy.Add(disposable);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cb219b23cdf4b314f94a27bca3cc8012
timeCreated: 1455373901
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
// for uGUI(from 4.6)
#if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5)
using System; // require keep for Windows Universal App
using UnityEngine;
using UnityEngine.EventSystems;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableDragTrigger : ObservableTriggerBase, IEventSystemHandler, IDragHandler
{
Subject<PointerEventData> onDrag;
void IDragHandler.OnDrag(PointerEventData eventData)
{
if (onDrag != null) onDrag.OnNext(eventData);
}
public IObservable<PointerEventData> OnDragAsObservable()
{
return onDrag ?? (onDrag = new Subject<PointerEventData>());
}
protected override void RaiseOnCompletedOnDestroy()
{
if (onDrag != null)
{
onDrag.OnCompleted();
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 79db090dc9e4db245821e8b89b0e208e
timeCreated: 1455373899
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
// for uGUI(from 4.6)
#if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5)
using System; // require keep for Windows Universal App
using UnityEngine;
using UnityEngine.EventSystems;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableDropTrigger : ObservableTriggerBase, IEventSystemHandler, IDropHandler
{
Subject<PointerEventData> onDrop;
void IDropHandler.OnDrop(PointerEventData eventData)
{
if (onDrop != null) onDrop.OnNext(eventData);
}
public IObservable<PointerEventData> OnDropAsObservable()
{
return onDrop ?? (onDrop = new Subject<PointerEventData>());
}
protected override void RaiseOnCompletedOnDestroy()
{
if (onDrop != null)
{
onDrop.OnCompleted();
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f2ffa8b5af3474446a310bb6aa0b180a
timeCreated: 1455373902
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
using System; // require keep for Windows Universal App
using UnityEngine;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableEnableTrigger : ObservableTriggerBase
{
Subject<Unit> onEnable;
/// <summary>This function is called when the object becomes enabled and active.</summary>
void OnEnable()
{
if (onEnable != null) onEnable.OnNext(Unit.Default);
}
/// <summary>This function is called when the object becomes enabled and active.</summary>
public IObservable<Unit> OnEnableAsObservable()
{
return onEnable ?? (onEnable = new Subject<Unit>());
}
Subject<Unit> onDisable;
/// <summary>This function is called when the behaviour becomes disabled () or inactive.</summary>
void OnDisable()
{
if (onDisable != null) onDisable.OnNext(Unit.Default);
}
/// <summary>This function is called when the behaviour becomes disabled () or inactive.</summary>
public IObservable<Unit> OnDisableAsObservable()
{
return onDisable ?? (onDisable = new Subject<Unit>());
}
protected override void RaiseOnCompletedOnDestroy()
{
if (onEnable != null)
{
onEnable.OnCompleted();
}
if (onDisable != null)
{
onDisable.OnCompleted();
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0d9c7eb607af1fd4aa0e15f52cc0543b
timeCreated: 1455373897
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
// for uGUI(from 4.6)
#if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5)
using System; // require keep for Windows Universal App
using UnityEngine;
using UnityEngine.EventSystems;
namespace UniRx.Triggers
{
[DisallowMultipleComponent]
public class ObservableEndDragTrigger : ObservableTriggerBase, IEventSystemHandler, IEndDragHandler
{
Subject<PointerEventData> onEndDrag;
void IEndDragHandler.OnEndDrag(PointerEventData eventData)
{
if (onEndDrag != null) onEndDrag.OnNext(eventData);
}
public IObservable<PointerEventData> OnEndDragAsObservable()
{
return onEndDrag ?? (onEndDrag = new Subject<PointerEventData>());
}
protected override void RaiseOnCompletedOnDestroy()
{
if (onEndDrag != null)
{
onEndDrag.OnCompleted();
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b8ce8424f238d6842bd8b09c0cca1ac4
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More