Weapos Rejiggered
Modified weapon scripts to eliminate specialized projectile behavior - allows charged weapons to have projectiles and modify weapon stats when charged Added machine gun weapon & VFX Moved third party code to Plugins folder to improve compile times
This commit is contained in:
parent
2a0fe34611
commit
dbc522a224
2105 changed files with 7033 additions and 1141 deletions
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UniRx
|
||||
{
|
||||
public static class AotSafeExtensions
|
||||
{
|
||||
public static IEnumerable<T> AsSafeEnumerable<T>(this IEnumerable<T> source)
|
||||
{
|
||||
var e = ((IEnumerable)source).GetEnumerator();
|
||||
using (e as IDisposable)
|
||||
{
|
||||
while (e.MoveNext())
|
||||
{
|
||||
yield return (T)e.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b54afb590b8d9f149aac39f480c8d4c0
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 245d77a29b1ece34e96bfc80f8c825d8
|
||||
timeCreated: 1455373897
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#if !(ENABLE_MONO_BLEEDING_EDGE_EDITOR || ENABLE_MONO_BLEEDING_EDGE_STANDALONE)
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e02a1bf45f8861048a6014cf7eab1825
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
#if (ENABLE_MONO_BLEEDING_EDGE_EDITOR || ENABLE_MONO_BLEEDING_EDGE_STANDALONE)
|
||||
|
||||
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<WWW> GetAwaiter(this WWW www)
|
||||
{
|
||||
return CoroutineAsyncBridge<WWW>.Start(www);
|
||||
}
|
||||
|
||||
public static CoroutineAsyncBridge GetAwaiter(this Coroutine coroutine)
|
||||
{
|
||||
return CoroutineAsyncBridge.Start(coroutine);
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 93ca3de3810199947871ab4a77014fa3
|
||||
timeCreated: 1475193276
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 049a195c5cca2694b854f7c6ffabe75f
|
||||
folderAsset: yes
|
||||
timeCreated: 1511817513
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 53917e87e91c0e4449402e5d85a04765
|
||||
timeCreated: 1455373899
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8706ef5a13e53ec46b4848a7eec5e826
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f0ecf366503cb0644bdd90934d24da62
|
||||
timeCreated: 1455373902
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b43f948e095c3e749a0506709be90d68
|
||||
timeCreated: 1468662620
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 063f79dc45f902c459f0955d27b445d7
|
||||
timeCreated: 1455373897
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 882166c30c3bff841b1e12d62c392e02
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
111
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/FrameInterval.cs
Normal file
111
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/FrameInterval.cs
Normal 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<T> value has the same Value and Interval as a specified FrameInterval<T> value.
|
||||
/// </summary>
|
||||
/// <param name="other">An object to compare to the current FrameInterval<T> value.</param>
|
||||
/// <returns>true if both FrameInterval<T> 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<T> values have the same Value and Interval.
|
||||
/// </summary>
|
||||
/// <param name="first">The first FrameInterval<T> value to compare.</param>
|
||||
/// <param name="second">The second FrameInterval<T> value to compare.</param>
|
||||
/// <returns>true if the first FrameInterval<T> value has the same Value and Interval as the second FrameInterval<T> 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<T> values don't have the same Value and Interval.
|
||||
/// </summary>
|
||||
/// <param name="first">The first FrameInterval<T> value to compare.</param>
|
||||
/// <param name="second">The second FrameInterval<T> value to compare.</param>
|
||||
/// <returns>true if the first FrameInterval<T> value has a different Value or Interval as the second FrameInterval<T> 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<T>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The System.Object to compare with the current FrameInterval<T>.</param>
|
||||
/// <returns>true if the specified System.Object is equal to the current FrameInterval<T>; 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<T> value.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for the current FrameInterval<T> 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<T> value.
|
||||
/// </summary>
|
||||
/// <returns>String representation of the current FrameInterval<T> value.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format(CultureInfo.CurrentCulture, "{0}@{1}", Value, Interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 266d1e44d71e7774c9abc5b23773e3f1
|
||||
timeCreated: 1467771656
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,324 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 13c690f353ea23141aca4090d28aaa9c
|
||||
timeCreated: 1455373897
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
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];
|
||||
var fieldInfo = obj.GetType().GetField(path, BindingFlags.IgnoreCase | BindingFlags.GetField | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
// If array, path = Array.data[index]
|
||||
if (fieldInfo == 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 (fieldInfo == null)
|
||||
{
|
||||
throw new Exception("Can't decode path, please report to UniRx's GitHub issues:" + string.Join(", ", paths));
|
||||
}
|
||||
|
||||
var v = fieldInfo.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
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6180f9fd2198dee44ae7f4a617529ffa
|
||||
timeCreated: 1455373899
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
143
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/LazyTask.cs
Normal file
143
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/LazyTask.cs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
// LazyTask is only for before MONO_BLEEDING_EDGE
|
||||
#if !(ENABLE_MONO_BLEEDING_EDGE_EDITOR || ENABLE_MONO_BLEEDING_EDGE_STANDALONE)
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq; // in future, should remove LINQ
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniRx
|
||||
{
|
||||
public abstract class LazyTask
|
||||
{
|
||||
public enum TaskStatus
|
||||
{
|
||||
WaitingToRun,
|
||||
Running,
|
||||
Completed,
|
||||
Canceled,
|
||||
Faulted
|
||||
}
|
||||
|
||||
public TaskStatus Status { get; protected set; }
|
||||
|
||||
protected readonly BooleanDisposable cancellation = new BooleanDisposable();
|
||||
|
||||
public abstract Coroutine Start();
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
if (Status == TaskStatus.WaitingToRun || Status == TaskStatus.Running)
|
||||
{
|
||||
Status = TaskStatus.Canceled;
|
||||
cancellation.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static LazyTask<T> FromResult<T>(T value)
|
||||
{
|
||||
return LazyTask<T>.FromResult(value);
|
||||
}
|
||||
|
||||
|
||||
public static Coroutine WhenAll(params LazyTask[] tasks)
|
||||
{
|
||||
return WhenAll(tasks.AsEnumerable());
|
||||
}
|
||||
|
||||
public static Coroutine WhenAll(IEnumerable<LazyTask> tasks)
|
||||
{
|
||||
var coroutines = tasks.Select(x => x.Start()).ToArray();
|
||||
|
||||
return MainThreadDispatcher.StartCoroutine(WhenAllCore(coroutines));
|
||||
}
|
||||
|
||||
static IEnumerator WhenAllCore(Coroutine[] coroutines)
|
||||
{
|
||||
foreach (var item in coroutines)
|
||||
{
|
||||
// wait sequential, but all coroutine is already started, it's parallel
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class LazyTask<T> : LazyTask
|
||||
{
|
||||
readonly IObservable<T> source;
|
||||
|
||||
T result;
|
||||
public T Result
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Status != TaskStatus.Completed) throw new InvalidOperationException("Task is not completed");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If faulted stock error. If completed or canceld, returns null.
|
||||
/// </summary>
|
||||
public Exception Exception { get; private set; }
|
||||
|
||||
public LazyTask(IObservable<T> source)
|
||||
{
|
||||
this.source = source;
|
||||
this.Status = TaskStatus.WaitingToRun;
|
||||
}
|
||||
|
||||
public override Coroutine Start()
|
||||
{
|
||||
if (Status != TaskStatus.WaitingToRun) throw new InvalidOperationException("Task already started");
|
||||
|
||||
Status = TaskStatus.Running;
|
||||
|
||||
var coroutine = source.StartAsCoroutine(
|
||||
onResult: x => { result = x; Status = TaskStatus.Completed; },
|
||||
onError: ex => { Exception = ex; Status = TaskStatus.Faulted; },
|
||||
cancel: new CancellationToken(cancellation));
|
||||
|
||||
return coroutine;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
switch (Status)
|
||||
{
|
||||
case TaskStatus.WaitingToRun:
|
||||
return "Status:WaitingToRun";
|
||||
case TaskStatus.Running:
|
||||
return "Status:Running";
|
||||
case TaskStatus.Completed:
|
||||
return "Status:Completed, Result:" + Result.ToString();
|
||||
case TaskStatus.Canceled:
|
||||
return "Status:Canceled";
|
||||
case TaskStatus.Faulted:
|
||||
return "Status:Faulted, Result:" + Result.ToString();
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static LazyTask<T> FromResult(T value)
|
||||
{
|
||||
var t = new LazyTask<T>(null);
|
||||
t.result = value; ;
|
||||
t.Status = TaskStatus.Completed;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LazyTaskExtensions
|
||||
{
|
||||
public static LazyTask<T> ToLazyTask<T>(this IObservable<T> source)
|
||||
{
|
||||
return new LazyTask<T>(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 894287b5c0263d14fbf00c5de21f3b81
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a7474e4acdc541340a1f566b2df46355
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,667 @@
|
|||
#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 (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
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
//var _ = EditorThreadDispatcher.Instance;
|
||||
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;
|
||||
|
||||
#if (ENABLE_MONO_BLEEDING_EDGE_EDITOR || ENABLE_MONO_BLEEDING_EDGE_STANDALONE)
|
||||
if (UniRxSynchronizationContext.AutoInstall)
|
||||
{
|
||||
SynchronizationContext.SetSynchronizationContext(new UniRxSynchronizationContext());
|
||||
}
|
||||
#endif
|
||||
|
||||
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>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c3cd207057515c4438a31a6a7b548fe7
|
||||
timeCreated: 1465903910
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -16000
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f141c5dc72b97084a85631367a946ee8
|
||||
timeCreated: 1455373902
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1164
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/Observable.Unity.cs
Normal file
1164
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/Observable.Unity.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c6ef0a186b9ceaf41af7f2a9f4006216
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,885 @@
|
|||
#if !UNITY_5_5_OR_NEWER
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniRx
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Note: TypedMonoBehaviour and ObservableMonoBehaviour cause some performance issues.</para>
|
||||
/// <para>This is legacy interface.</para>
|
||||
/// <para>I recommend use ObservableTriggers(UniRx.Triggers) instead.</para>
|
||||
/// <para>More information, see github page.</para>
|
||||
/// </summary>
|
||||
[Obsolete("ObservableMonoBehaviour is legacy component. use triggers instead")]
|
||||
public class ObservableMonoBehaviour : TypedMonoBehaviour
|
||||
{
|
||||
bool calledAwake = false;
|
||||
Subject<Unit> awake;
|
||||
|
||||
/// <summary>Awake is called when the script instance is being loaded.</summary>
|
||||
public override void Awake()
|
||||
{
|
||||
calledAwake = true;
|
||||
if (awake != null) { awake.OnNext(Unit.Default); awake.OnCompleted(); }
|
||||
}
|
||||
|
||||
/// <summary>Awake is called when the script instance is being loaded.</summary>
|
||||
public IObservable<Unit> AwakeAsObservable()
|
||||
{
|
||||
if (calledAwake) return Observable.Return(Unit.Default);
|
||||
return awake ?? (awake = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> fixedUpdate;
|
||||
|
||||
/// <summary>This function is called every fixed framerate frame, if the MonoBehaviour is enabled.</summary>
|
||||
public override void FixedUpdate()
|
||||
{
|
||||
if (fixedUpdate != null) fixedUpdate.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>This function is called every fixed framerate frame, if the MonoBehaviour is enabled.</summary>
|
||||
public IObservable<Unit> FixedUpdateAsObservable()
|
||||
{
|
||||
return fixedUpdate ?? (fixedUpdate = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> lateUpdate;
|
||||
|
||||
/// <summary>LateUpdate is called every frame, if the Behaviour is enabled.</summary>
|
||||
public override void LateUpdate()
|
||||
{
|
||||
if (lateUpdate != null) lateUpdate.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>LateUpdate is called every frame, if the Behaviour is enabled.</summary>
|
||||
public IObservable<Unit> LateUpdateAsObservable()
|
||||
{
|
||||
return lateUpdate ?? (lateUpdate = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<int> onAnimatorIK;
|
||||
|
||||
/// <summary>Callback for setting up animation IK (inverse kinematics).</summary>
|
||||
public override 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>
|
||||
public override 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>());
|
||||
}
|
||||
|
||||
Subject<bool> onApplicationFocus;
|
||||
|
||||
/// <summary>Sent to all game objects when the player gets or loses focus.</summary>
|
||||
public override void OnApplicationFocus(bool focus)
|
||||
{
|
||||
if (onApplicationFocus != null) onApplicationFocus.OnNext(focus);
|
||||
}
|
||||
|
||||
/// <summary>Sent to all game objects when the player gets or loses focus.</summary>
|
||||
public IObservable<bool> OnApplicationFocusAsObservable()
|
||||
{
|
||||
return onApplicationFocus ?? (onApplicationFocus = new Subject<bool>());
|
||||
}
|
||||
|
||||
Subject<bool> onApplicationPause;
|
||||
|
||||
/// <summary>Sent to all game objects when the player pauses.</summary>
|
||||
public override void OnApplicationPause(bool pause)
|
||||
{
|
||||
if (onApplicationPause != null) onApplicationPause.OnNext(pause);
|
||||
}
|
||||
|
||||
/// <summary>Sent to all game objects when the player pauses.</summary>
|
||||
public IObservable<bool> OnApplicationPauseAsObservable()
|
||||
{
|
||||
return onApplicationPause ?? (onApplicationPause = new Subject<bool>());
|
||||
}
|
||||
|
||||
Subject<Unit> onApplicationQuit;
|
||||
|
||||
/// <summary>Sent to all game objects before the application is quit.</summary>
|
||||
public override void OnApplicationQuit()
|
||||
{
|
||||
if (onApplicationQuit != null) onApplicationQuit.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>Sent to all game objects before the application is quit.</summary>
|
||||
public IObservable<Unit> OnApplicationQuitAsObservable()
|
||||
{
|
||||
return onApplicationQuit ?? (onApplicationQuit = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Tuple<float[], int>> onAudioFilterRead;
|
||||
|
||||
/// <summary>If OnAudioFilterRead is implemented, Unity will insert a custom filter into the audio DSP chain.</summary>
|
||||
public override void OnAudioFilterRead(float[] data, int channels)
|
||||
{
|
||||
if (onAudioFilterRead != null) onAudioFilterRead.OnNext(Tuple.Create(data, channels));
|
||||
}
|
||||
|
||||
/// <summary>If OnAudioFilterRead is implemented, Unity will insert a custom filter into the audio DSP chain.</summary>
|
||||
public IObservable<Tuple<float[], int>> OnAudioFilterReadAsObservable()
|
||||
{
|
||||
return onAudioFilterRead ?? (onAudioFilterRead = new Subject<Tuple<float[], int>>());
|
||||
}
|
||||
|
||||
Subject<Unit> onBecameInvisible;
|
||||
|
||||
/// <summary>OnBecameInvisible is called when the renderer is no longer visible by any camera.</summary>
|
||||
public override void OnBecameInvisible()
|
||||
{
|
||||
if (onBecameInvisible != null) onBecameInvisible.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnBecameInvisible is called when the renderer is no longer visible by any camera.</summary>
|
||||
public IObservable<Unit> OnBecameInvisibleAsObservable()
|
||||
{
|
||||
return onBecameInvisible ?? (onBecameInvisible = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onBecameVisible;
|
||||
|
||||
/// <summary>OnBecameVisible is called when the renderer became visible by any camera.</summary>
|
||||
public override void OnBecameVisible()
|
||||
{
|
||||
if (onBecameVisible != null) onBecameVisible.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnBecameVisible is called when the renderer became visible by any camera.</summary>
|
||||
public IObservable<Unit> OnBecameVisibleAsObservable()
|
||||
{
|
||||
return onBecameVisible ?? (onBecameVisible = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Collision> onCollisionEnter;
|
||||
|
||||
/// <summary>OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider.</summary>
|
||||
public override 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<Collision2D> onCollisionEnter2D;
|
||||
|
||||
/// <summary>Sent when an incoming collider makes contact with this object's collider (2D physics only).</summary>
|
||||
public override 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<Collision> onCollisionExit;
|
||||
|
||||
/// <summary>OnCollisionExit is called when this collider/rigidbody has stopped touching another rigidbody/collider.</summary>
|
||||
public override 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<Collision2D> onCollisionExit2D;
|
||||
|
||||
/// <summary>Sent when a collider on another object stops touching this object's collider (2D physics only).</summary>
|
||||
public override 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<Collision> onCollisionStay;
|
||||
|
||||
/// <summary>OnCollisionStay is called once per frame for every collider/rigidbody that is touching rigidbody/collider.</summary>
|
||||
public override 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>());
|
||||
}
|
||||
|
||||
Subject<Collision2D> onCollisionStay2D;
|
||||
|
||||
/// <summary>Sent each frame where a collider on another object is touching this object's collider (2D physics only).</summary>
|
||||
public override 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>());
|
||||
}
|
||||
|
||||
Subject<Unit> onConnectedToServer;
|
||||
|
||||
/// <summary>Called on the client when you have successfully connected to a server.</summary>
|
||||
public override void OnConnectedToServer()
|
||||
{
|
||||
if (onConnectedToServer != null) onConnectedToServer.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>Called on the client when you have successfully connected to a server.</summary>
|
||||
public IObservable<Unit> OnConnectedToServerAsObservable()
|
||||
{
|
||||
return onConnectedToServer ?? (onConnectedToServer = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<ControllerColliderHit> onControllerColliderHit;
|
||||
|
||||
/// <summary>OnControllerColliderHit is called when the controller hits a collider while performing a Move.</summary>
|
||||
public override void OnControllerColliderHit(ControllerColliderHit hit)
|
||||
{
|
||||
if (onControllerColliderHit != null) onControllerColliderHit.OnNext(hit);
|
||||
}
|
||||
|
||||
/// <summary>OnControllerColliderHit is called when the controller hits a collider while performing a Move.</summary>
|
||||
public IObservable<ControllerColliderHit> OnControllerColliderHitAsObservable()
|
||||
{
|
||||
return onControllerColliderHit ?? (onControllerColliderHit = new Subject<ControllerColliderHit>());
|
||||
}
|
||||
|
||||
bool calledDestroy = false;
|
||||
Subject<Unit> onDestroy;
|
||||
|
||||
/// <summary>This function is called when the MonoBehaviour will be destroyed.</summary>
|
||||
public override void OnDestroy()
|
||||
{
|
||||
calledDestroy = true;
|
||||
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>());
|
||||
}
|
||||
|
||||
Subject<Unit> onDisable;
|
||||
|
||||
/// <summary>This function is called when the behaviour becomes disabled () or inactive.</summary>
|
||||
public override 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>());
|
||||
}
|
||||
|
||||
Subject<Unit> onDrawGizmos;
|
||||
|
||||
/// <summary>Implement OnDrawGizmos if you want to draw gizmos that are also pickable and always drawn.</summary>
|
||||
public override void OnDrawGizmos()
|
||||
{
|
||||
if (onDrawGizmos != null) onDrawGizmos.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>Implement OnDrawGizmos if you want to draw gizmos that are also pickable and always drawn.</summary>
|
||||
public IObservable<Unit> OnDrawGizmosAsObservable()
|
||||
{
|
||||
return onDrawGizmos ?? (onDrawGizmos = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onDrawGizmosSelected;
|
||||
|
||||
/// <summary>Implement this OnDrawGizmosSelected if you want to draw gizmos only if the object is selected.</summary>
|
||||
public override void OnDrawGizmosSelected()
|
||||
{
|
||||
if (onDrawGizmosSelected != null) onDrawGizmosSelected.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>Implement this OnDrawGizmosSelected if you want to draw gizmos only if the object is selected.</summary>
|
||||
public IObservable<Unit> OnDrawGizmosSelectedAsObservable()
|
||||
{
|
||||
return onDrawGizmosSelected ?? (onDrawGizmosSelected = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onEnable;
|
||||
|
||||
/// <summary>This function is called when the object becomes enabled and active.</summary>
|
||||
public override 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>());
|
||||
}
|
||||
|
||||
#if FALSE // OnGUI called multiple time per frame update and it cause performance issue, If you want to need OnGUI, copy & paste this code on your MonoBehaviour
|
||||
|
||||
Subject<Unit> onGUI;
|
||||
|
||||
/// <summary>OnGUI is called for rendering and handling GUI events.</summary>
|
||||
public override void OnGUI()
|
||||
{
|
||||
if (onGUI != null) onGUI.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnGUI is called for rendering and handling GUI events.</summary>
|
||||
public IObservable<Unit> OnGUIAsObservable()
|
||||
{
|
||||
return onGUI ?? (onGUI = new Subject<Unit>());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Subject<float> onJointBreak;
|
||||
|
||||
/// <summary>Called when a joint attached to the same game object broke.</summary>
|
||||
public override void OnJointBreak(float breakForce)
|
||||
{
|
||||
if (onJointBreak != null) onJointBreak.OnNext(breakForce);
|
||||
}
|
||||
|
||||
/// <summary>Called when a joint attached to the same game object broke.</summary>
|
||||
public IObservable<float> OnJointBreakAsObservable()
|
||||
{
|
||||
return onJointBreak ?? (onJointBreak = new Subject<float>());
|
||||
}
|
||||
|
||||
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
|
||||
|
||||
Subject<Unit> onMouseDown;
|
||||
|
||||
/// <summary>OnMouseDown is called when the user has pressed the mouse button while over the GUIElement or Collider.</summary>
|
||||
public override void OnMouseDown()
|
||||
{
|
||||
if (onMouseDown != null) onMouseDown.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnMouseDown is called when the user has pressed the mouse button while over the GUIElement or Collider.</summary>
|
||||
public IObservable<Unit> OnMouseDownAsObservable()
|
||||
{
|
||||
return onMouseDown ?? (onMouseDown = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onMouseDrag;
|
||||
|
||||
/// <summary>OnMouseDrag is called when the user has clicked on a GUIElement or Collider and is still holding down the mouse.</summary>
|
||||
public override void OnMouseDrag()
|
||||
{
|
||||
if (onMouseDrag != null) onMouseDrag.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnMouseDrag is called when the user has clicked on a GUIElement or Collider and is still holding down the mouse.</summary>
|
||||
public IObservable<Unit> OnMouseDragAsObservable()
|
||||
{
|
||||
return onMouseDrag ?? (onMouseDrag = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onMouseEnter;
|
||||
|
||||
/// <summary>OnMouseEnter is called when the mouse entered the GUIElement or Collider.</summary>
|
||||
public override void OnMouseEnter()
|
||||
{
|
||||
if (onMouseEnter != null) onMouseEnter.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnMouseEnter is called when the mouse entered the GUIElement or Collider.</summary>
|
||||
public IObservable<Unit> OnMouseEnterAsObservable()
|
||||
{
|
||||
return onMouseEnter ?? (onMouseEnter = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onMouseExit;
|
||||
|
||||
/// <summary>OnMouseExit is called when the mouse is not any longer over the GUIElement or Collider.</summary>
|
||||
public override void OnMouseExit()
|
||||
{
|
||||
if (onMouseExit != null) onMouseExit.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnMouseExit is called when the mouse is not any longer over the GUIElement or Collider.</summary>
|
||||
public IObservable<Unit> OnMouseExitAsObservable()
|
||||
{
|
||||
return onMouseExit ?? (onMouseExit = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onMouseOver;
|
||||
|
||||
/// <summary>OnMouseOver is called every frame while the mouse is over the GUIElement or Collider.</summary>
|
||||
public override void OnMouseOver()
|
||||
{
|
||||
if (onMouseOver != null) onMouseOver.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnMouseOver is called every frame while the mouse is over the GUIElement or Collider.</summary>
|
||||
public IObservable<Unit> OnMouseOverAsObservable()
|
||||
{
|
||||
return onMouseOver ?? (onMouseOver = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onMouseUp;
|
||||
|
||||
/// <summary>OnMouseUp is called when the user has released the mouse button.</summary>
|
||||
public override void OnMouseUp()
|
||||
{
|
||||
if (onMouseUp != null) onMouseUp.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnMouseUp is called when the user has released the mouse button.</summary>
|
||||
public IObservable<Unit> OnMouseUpAsObservable()
|
||||
{
|
||||
return onMouseUp ?? (onMouseUp = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onMouseUpAsButton;
|
||||
|
||||
/// <summary>OnMouseUpAsButton is only called when the mouse is released over the same GUIElement or Collider as it was pressed.</summary>
|
||||
public override void OnMouseUpAsButton()
|
||||
{
|
||||
if (onMouseUpAsButton != null) onMouseUpAsButton.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnMouseUpAsButton is only called when the mouse is released over the same GUIElement or Collider as it was pressed.</summary>
|
||||
public IObservable<Unit> OnMouseUpAsButtonAsObservable()
|
||||
{
|
||||
return onMouseUpAsButton ?? (onMouseUpAsButton = new Subject<Unit>());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Subject<GameObject> onParticleCollision;
|
||||
|
||||
/// <summary>OnParticleCollision is called when a particle hits a collider.</summary>
|
||||
public override void OnParticleCollision(GameObject other)
|
||||
{
|
||||
if (onParticleCollision != null) onParticleCollision.OnNext(other);
|
||||
}
|
||||
|
||||
/// <summary>OnParticleCollision is called when a particle hits a collider.</summary>
|
||||
public IObservable<GameObject> OnParticleCollisionAsObservable()
|
||||
{
|
||||
return onParticleCollision ?? (onParticleCollision = new Subject<GameObject>());
|
||||
}
|
||||
|
||||
Subject<Unit> onPostRender;
|
||||
|
||||
/// <summary>OnPostRender is called after a camera finished rendering the scene.</summary>
|
||||
public override void OnPostRender()
|
||||
{
|
||||
if (onPostRender != null) onPostRender.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnPostRender is called after a camera finished rendering the scene.</summary>
|
||||
public IObservable<Unit> OnPostRenderAsObservable()
|
||||
{
|
||||
return onPostRender ?? (onPostRender = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onPreCull;
|
||||
|
||||
/// <summary>OnPreCull is called before a camera culls the scene.</summary>
|
||||
public override void OnPreCull()
|
||||
{
|
||||
if (onPreCull != null) onPreCull.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnPreCull is called before a camera culls the scene.</summary>
|
||||
public IObservable<Unit> OnPreCullAsObservable()
|
||||
{
|
||||
return onPreCull ?? (onPreCull = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onPreRender;
|
||||
|
||||
/// <summary>OnPreRender is called before a camera starts rendering the scene.</summary>
|
||||
public override void OnPreRender()
|
||||
{
|
||||
if (onPreRender != null) onPreRender.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnPreRender is called before a camera starts rendering the scene.</summary>
|
||||
public IObservable<Unit> OnPreRenderAsObservable()
|
||||
{
|
||||
return onPreRender ?? (onPreRender = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Tuple<RenderTexture, RenderTexture>> onRenderImage;
|
||||
|
||||
/// <summary>OnRenderImage is called after all rendering is complete to render image.</summary>
|
||||
public override void OnRenderImage(RenderTexture src, RenderTexture dest)
|
||||
{
|
||||
if (onRenderImage != null) onRenderImage.OnNext(Tuple.Create(src, dest));
|
||||
}
|
||||
|
||||
/// <summary>OnRenderImage is called after all rendering is complete to render image.</summary>
|
||||
public IObservable<Tuple<RenderTexture, RenderTexture>> OnRenderImageAsObservable()
|
||||
{
|
||||
return onRenderImage ?? (onRenderImage = new Subject<Tuple<RenderTexture, RenderTexture>>());
|
||||
}
|
||||
|
||||
Subject<Unit> onRenderObject;
|
||||
|
||||
/// <summary>OnRenderObject is called after camera has rendered the scene.</summary>
|
||||
public override void OnRenderObject()
|
||||
{
|
||||
if (onRenderObject != null) onRenderObject.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnRenderObject is called after camera has rendered the scene.</summary>
|
||||
public IObservable<Unit> OnRenderObjectAsObservable()
|
||||
{
|
||||
return onRenderObject ?? (onRenderObject = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onServerInitialized;
|
||||
|
||||
/// <summary>Called on the server whenever a Network.InitializeServer was invoked and has completed.</summary>
|
||||
public override void OnServerInitialized()
|
||||
{
|
||||
if (onServerInitialized != null) onServerInitialized.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>Called on the server whenever a Network.InitializeServer was invoked and has completed.</summary>
|
||||
public IObservable<Unit> OnServerInitializedAsObservable()
|
||||
{
|
||||
return onServerInitialized ?? (onServerInitialized = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Collider> onTriggerEnter;
|
||||
|
||||
/// <summary>OnTriggerEnter is called when the Collider other enters the trigger.</summary>
|
||||
public override void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (onTriggerEnter != null) onTriggerEnter.OnNext(other);
|
||||
}
|
||||
|
||||
/// <summary>OnTriggerEnter is called when the Collider other enters the trigger.</summary>
|
||||
public IObservable<Collider> OnTriggerEnterAsObservable()
|
||||
{
|
||||
return onTriggerEnter ?? (onTriggerEnter = new Subject<Collider>());
|
||||
}
|
||||
|
||||
Subject<Collider2D> onTriggerEnter2D;
|
||||
|
||||
/// <summary>Sent when another object enters a trigger collider attached to this object (2D physics only).</summary>
|
||||
public override void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
if (onTriggerEnter2D != null) onTriggerEnter2D.OnNext(other);
|
||||
}
|
||||
|
||||
/// <summary>Sent when another object enters a trigger collider attached to this object (2D physics only).</summary>
|
||||
public IObservable<Collider2D> OnTriggerEnter2DAsObservable()
|
||||
{
|
||||
return onTriggerEnter2D ?? (onTriggerEnter2D = new Subject<Collider2D>());
|
||||
}
|
||||
|
||||
Subject<Collider> onTriggerExit;
|
||||
|
||||
/// <summary>OnTriggerExit is called when the Collider other has stopped touching the trigger.</summary>
|
||||
public override void OnTriggerExit(Collider other)
|
||||
{
|
||||
if (onTriggerExit != null) onTriggerExit.OnNext(other);
|
||||
}
|
||||
|
||||
/// <summary>OnTriggerExit is called when the Collider other has stopped touching the trigger.</summary>
|
||||
public IObservable<Collider> OnTriggerExitAsObservable()
|
||||
{
|
||||
return onTriggerExit ?? (onTriggerExit = new Subject<Collider>());
|
||||
}
|
||||
|
||||
Subject<Collider2D> onTriggerExit2D;
|
||||
|
||||
/// <summary>Sent when another object leaves a trigger collider attached to this object (2D physics only).</summary>
|
||||
public override void OnTriggerExit2D(Collider2D other)
|
||||
{
|
||||
if (onTriggerExit2D != null) onTriggerExit2D.OnNext(other);
|
||||
}
|
||||
|
||||
/// <summary>Sent when another object leaves a trigger collider attached to this object (2D physics only).</summary>
|
||||
public IObservable<Collider2D> OnTriggerExit2DAsObservable()
|
||||
{
|
||||
return onTriggerExit2D ?? (onTriggerExit2D = new Subject<Collider2D>());
|
||||
}
|
||||
|
||||
Subject<Collider> onTriggerStay;
|
||||
|
||||
/// <summary>OnTriggerStay is called once per frame for every Collider other that is touching the trigger.</summary>
|
||||
public override void OnTriggerStay(Collider other)
|
||||
{
|
||||
if (onTriggerStay != null) onTriggerStay.OnNext(other);
|
||||
}
|
||||
|
||||
/// <summary>OnTriggerStay is called once per frame for every Collider other that is touching the trigger.</summary>
|
||||
public IObservable<Collider> OnTriggerStayAsObservable()
|
||||
{
|
||||
return onTriggerStay ?? (onTriggerStay = new Subject<Collider>());
|
||||
}
|
||||
|
||||
Subject<Collider2D> onTriggerStay2D;
|
||||
|
||||
/// <summary>Sent each frame where another object is within a trigger collider attached to this object (2D physics only).</summary>
|
||||
public override void OnTriggerStay2D(Collider2D other)
|
||||
{
|
||||
if (onTriggerStay2D != null) onTriggerStay2D.OnNext(other);
|
||||
}
|
||||
|
||||
/// <summary>Sent each frame where another object is within a trigger collider attached to this object (2D physics only).</summary>
|
||||
public IObservable<Collider2D> OnTriggerStay2DAsObservable()
|
||||
{
|
||||
return onTriggerStay2D ?? (onTriggerStay2D = new Subject<Collider2D>());
|
||||
}
|
||||
|
||||
Subject<Unit> onValidate;
|
||||
|
||||
/// <summary>This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only).</summary>
|
||||
public override void OnValidate()
|
||||
{
|
||||
if (onValidate != null) onValidate.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only).</summary>
|
||||
public IObservable<Unit> OnValidateAsObservable()
|
||||
{
|
||||
return onValidate ?? (onValidate = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> onWillRenderObject;
|
||||
|
||||
/// <summary>OnWillRenderObject is called once for each camera if the object is visible.</summary>
|
||||
public override void OnWillRenderObject()
|
||||
{
|
||||
if (onWillRenderObject != null) onWillRenderObject.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>OnWillRenderObject is called once for each camera if the object is visible.</summary>
|
||||
public IObservable<Unit> OnWillRenderObjectAsObservable()
|
||||
{
|
||||
return onWillRenderObject ?? (onWillRenderObject = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> reset;
|
||||
|
||||
/// <summary>Reset to default values.</summary>
|
||||
public override void Reset()
|
||||
{
|
||||
if (reset != null) reset.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>Reset to default values.</summary>
|
||||
public IObservable<Unit> ResetAsObservable()
|
||||
{
|
||||
return reset ?? (reset = new Subject<Unit>());
|
||||
}
|
||||
|
||||
bool calledStart = false;
|
||||
Subject<Unit> start;
|
||||
|
||||
/// <summary>Start is called on the frame when a script is enabled just before any of the Update methods is called the first time.</summary>
|
||||
public override void Start()
|
||||
{
|
||||
calledStart = true;
|
||||
if (start != null) { start.OnNext(Unit.Default); start.OnCompleted(); }
|
||||
}
|
||||
|
||||
/// <summary>Start is called on the frame when a script is enabled just before any of the Update methods is called the first time.</summary>
|
||||
public IObservable<Unit> StartAsObservable()
|
||||
{
|
||||
if (calledStart) return Observable.Return(Unit.Default);
|
||||
return start ?? (start = new Subject<Unit>());
|
||||
}
|
||||
|
||||
Subject<Unit> update;
|
||||
|
||||
/// <summary>Update is called every frame, if the MonoBehaviour is enabled.</summary>
|
||||
public override void Update()
|
||||
{
|
||||
if (update != null) update.OnNext(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>Update is called every frame, if the MonoBehaviour is enabled.</summary>
|
||||
public IObservable<Unit> UpdateAsObservable()
|
||||
{
|
||||
return update ?? (update = new Subject<Unit>());
|
||||
}
|
||||
|
||||
#if !(UNITY_METRO || UNITY_WP8 || UNITY_NACL_CHROME || UNITY_WEBGL)
|
||||
|
||||
Subject<NetworkDisconnection> onDisconnectedFromServer;
|
||||
|
||||
/// <summary>Called on the client when the connection was lost or you disconnected from the server.</summary>
|
||||
public override void OnDisconnectedFromServer(NetworkDisconnection info)
|
||||
{
|
||||
if (onDisconnectedFromServer != null) onDisconnectedFromServer.OnNext(info);
|
||||
}
|
||||
|
||||
/// <summary>Called on the client when the connection was lost or you disconnected from the server.</summary>
|
||||
public IObservable<NetworkDisconnection> OnDisconnectedFromServerAsObservable()
|
||||
{
|
||||
return onDisconnectedFromServer ?? (onDisconnectedFromServer = new Subject<NetworkDisconnection>());
|
||||
}
|
||||
|
||||
Subject<NetworkConnectionError> onFailedToConnect;
|
||||
|
||||
/// <summary>Called on the client when a connection attempt fails for some reason.</summary>
|
||||
public override void OnFailedToConnect(NetworkConnectionError error)
|
||||
{
|
||||
if (onFailedToConnect != null) onFailedToConnect.OnNext(error);
|
||||
}
|
||||
|
||||
/// <summary>Called on the client when a connection attempt fails for some reason.</summary>
|
||||
public IObservable<NetworkConnectionError> OnFailedToConnectAsObservable()
|
||||
{
|
||||
return onFailedToConnect ?? (onFailedToConnect = new Subject<NetworkConnectionError>());
|
||||
}
|
||||
|
||||
Subject<NetworkConnectionError> onFailedToConnectToMasterServer;
|
||||
|
||||
/// <summary>Called on clients or servers when there is a problem connecting to the MasterServer.</summary>
|
||||
public override void OnFailedToConnectToMasterServer(NetworkConnectionError info)
|
||||
{
|
||||
if (onFailedToConnectToMasterServer != null) onFailedToConnectToMasterServer.OnNext(info);
|
||||
}
|
||||
|
||||
/// <summary>Called on clients or servers when there is a problem connecting to the MasterServer.</summary>
|
||||
public IObservable<NetworkConnectionError> OnFailedToConnectToMasterServerAsObservable()
|
||||
{
|
||||
return onFailedToConnectToMasterServer ?? (onFailedToConnectToMasterServer = new Subject<NetworkConnectionError>());
|
||||
}
|
||||
|
||||
Subject<MasterServerEvent> onMasterServerEvent;
|
||||
|
||||
/// <summary>Called on clients or servers when reporting events from the MasterServer.</summary>
|
||||
public override void OnMasterServerEvent(MasterServerEvent msEvent)
|
||||
{
|
||||
if (onMasterServerEvent != null) onMasterServerEvent.OnNext(msEvent);
|
||||
}
|
||||
|
||||
/// <summary>Called on clients or servers when reporting events from the MasterServer.</summary>
|
||||
public IObservable<MasterServerEvent> OnMasterServerEventAsObservable()
|
||||
{
|
||||
return onMasterServerEvent ?? (onMasterServerEvent = new Subject<MasterServerEvent>());
|
||||
}
|
||||
|
||||
Subject<NetworkMessageInfo> onNetworkInstantiate;
|
||||
|
||||
/// <summary>Called on objects which have been network instantiated with Network.Instantiate.</summary>
|
||||
public override void OnNetworkInstantiate(NetworkMessageInfo info)
|
||||
{
|
||||
if (onNetworkInstantiate != null) onNetworkInstantiate.OnNext(info);
|
||||
}
|
||||
|
||||
/// <summary>Called on objects which have been network instantiated with Network.Instantiate.</summary>
|
||||
public IObservable<NetworkMessageInfo> OnNetworkInstantiateAsObservable()
|
||||
{
|
||||
return onNetworkInstantiate ?? (onNetworkInstantiate = new Subject<NetworkMessageInfo>());
|
||||
}
|
||||
|
||||
Subject<NetworkPlayer> onPlayerConnected;
|
||||
|
||||
/// <summary>Called on the server whenever a new player has successfully connected.</summary>
|
||||
public override void OnPlayerConnected(NetworkPlayer player)
|
||||
{
|
||||
if (onPlayerConnected != null) onPlayerConnected.OnNext(player);
|
||||
}
|
||||
|
||||
/// <summary>Called on the server whenever a new player has successfully connected.</summary>
|
||||
public IObservable<NetworkPlayer> OnPlayerConnectedAsObservable()
|
||||
{
|
||||
return onPlayerConnected ?? (onPlayerConnected = new Subject<NetworkPlayer>());
|
||||
}
|
||||
|
||||
Subject<NetworkPlayer> onPlayerDisconnected;
|
||||
|
||||
/// <summary>Called on the server whenever a player disconnected from the server.</summary>
|
||||
public override void OnPlayerDisconnected(NetworkPlayer player)
|
||||
{
|
||||
if (onPlayerDisconnected != null) onPlayerDisconnected.OnNext(player);
|
||||
}
|
||||
|
||||
/// <summary>Called on the server whenever a player disconnected from the server.</summary>
|
||||
public IObservable<NetworkPlayer> OnPlayerDisconnectedAsObservable()
|
||||
{
|
||||
return onPlayerDisconnected ?? (onPlayerDisconnected = new Subject<NetworkPlayer>());
|
||||
}
|
||||
|
||||
Subject<Tuple<BitStream, NetworkMessageInfo>> onSerializeNetworkView;
|
||||
|
||||
/// <summary>Used to customize synchronization of variables in a script watched by a network view.</summary>
|
||||
public override void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
|
||||
{
|
||||
if (onSerializeNetworkView != null) onSerializeNetworkView.OnNext(Tuple.Create(stream, info));
|
||||
}
|
||||
|
||||
/// <summary>Used to customize synchronization of variables in a script watched by a network view.</summary>
|
||||
public IObservable<Tuple<BitStream, NetworkMessageInfo>> OnSerializeNetworkViewAsObservable()
|
||||
{
|
||||
return onSerializeNetworkView ?? (onSerializeNetworkView = new Subject<Tuple<BitStream, NetworkMessageInfo>>());
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// above code is generated from template
|
||||
|
||||
/*
|
||||
var template = @"Subject<Unit> {0};
|
||||
|
||||
/// <summary>{1}</summary>
|
||||
public override void {2}()
|
||||
{{
|
||||
if ({0} != null) {0}.OnNext(Unit.Default);
|
||||
}}
|
||||
|
||||
/// <summary>{1}</summary>
|
||||
public IObservable<Unit> {2}AsObservable()
|
||||
{{
|
||||
return {0} ?? ({0} = new Subject<Unit>());
|
||||
}}";
|
||||
*/
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e57124d833524f44683d4699fadca8e0
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8741793924a6c2f4ea22ba27031d531f
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 55985ea7317c22745bec1d8c9bbe3be4
|
||||
folderAsset: yes
|
||||
timeCreated: 1511817513
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 74a2b6b8c63d1144f914c7f0d6719a36
|
||||
timeCreated: 1467771656
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 868f75a703f1a944a801ab9c9b4512aa
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ecfef95eedf36c2448944fb8932f682c
|
||||
timeCreated: 1455373902
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a731a1a74be20a04a9d7dedc5ceefab2
|
||||
timeCreated: 1467771656
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a55cce9ef638364409d1227a25a32421
|
||||
timeCreated: 1467771656
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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 (ENABLE_MONO_BLEEDING_EDGE_EDITOR || ENABLE_MONO_BLEEDING_EDGE_STANDALONE)
|
||||
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 (ENABLE_MONO_BLEEDING_EDGE_EDITOR || ENABLE_MONO_BLEEDING_EDGE_STANDALONE)
|
||||
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(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e83ddad992535fb4f8a68a1e7ef8be60
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 93507e8a72a71094f870c8dbe1e5bed8
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e04c7fc1929a3db458bf7ae31bcd9e55
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: da3fd97518766ab43827991b7b5d4270
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3ec92e777b0b4d949967b0663ce8bee8
|
||||
timeCreated: 1455373898
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2c4ef0bfcfe787543999c7a6cda03c07
|
||||
timeCreated: 1455373898
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c27be0a585d78a944bccd31b86ee6722
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
234
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/PresenterBase.cs
Normal file
234
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/PresenterBase.cs
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
/*
|
||||
PresenterBase works enough, but too complex.
|
||||
You can use simple Initialize method and call parent to child, it works for most scenario.
|
||||
So I don't recommend using PresenterBase, sorry.
|
||||
*/
|
||||
|
||||
namespace UniRx
|
||||
{
|
||||
// InEditor : Construct Children Dependency
|
||||
// Awake : Construct Parent Dependency
|
||||
// Start(Capture phase) : Parent to Child, pass argument
|
||||
// Start(Bubbling phase) : Child to Parent, initialize(like constructor)
|
||||
|
||||
/// <summary>
|
||||
/// [Obsolete]Infrastructure interface for PresenterBase`T
|
||||
/// </summary>
|
||||
public interface IPresenter
|
||||
{
|
||||
IPresenter Parent { get; }
|
||||
GameObject gameObject { get; }
|
||||
void RegisterParent(IPresenter parent);
|
||||
void InitializeCore();
|
||||
void StartCapturePhase();
|
||||
void Awake();
|
||||
void ForceInitialize(object argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [Obsolete]PresenterBase can control dependency of presenter's hierarchy.
|
||||
/// </summary>
|
||||
public abstract class PresenterBase : PresenterBase<Unit>
|
||||
{
|
||||
protected sealed override void BeforeInitialize(Unit argument)
|
||||
{
|
||||
BeforeInitialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as Start but called before children initialized, it's chance for propagate argument to children.
|
||||
/// </summary>
|
||||
protected abstract void BeforeInitialize();
|
||||
|
||||
protected override void Initialize(Unit argument)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force Start BeforeInitialize/Initialize. If you create presenter dynamically, maybe useful.
|
||||
/// </summary>
|
||||
public void ForceInitialize()
|
||||
{
|
||||
ForceInitialize(Unit.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as Start but called after all children are initialized.
|
||||
/// </summary>
|
||||
protected abstract void Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [Obsolete]PresenterBase can control dependency of presenter's hierarchy.
|
||||
/// </summary>
|
||||
public abstract class PresenterBase<T> : MonoBehaviour, IPresenter
|
||||
{
|
||||
protected static readonly IPresenter[] EmptyChildren = new IPresenter[0];
|
||||
|
||||
int childrenCount = 0;
|
||||
int currentCalledCount = 0;
|
||||
bool isAwaken = false;
|
||||
bool isInitialized = false;
|
||||
bool isStartedCapturePhase = false;
|
||||
Subject<Unit> initializeSubject = null;
|
||||
IPresenter[] children = null; // set from awake
|
||||
|
||||
IPresenter parent = null;
|
||||
T argument = default(T);
|
||||
|
||||
public IPresenter Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Observable sequence called after initialize completed.
|
||||
/// </summary>
|
||||
public IObservable<Unit> InitializeAsObservable()
|
||||
{
|
||||
if (isInitialized) return Observable.Return(Unit.Default);
|
||||
return initializeSubject ?? (initializeSubject = new Subject<Unit>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Propagate(Set) argument.
|
||||
/// </summary>
|
||||
public void PropagateArgument(T argument)
|
||||
{
|
||||
this.argument = argument;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dependency on hierarchy of this presenter. If Children is empty, you can return this.EmptyChildren.
|
||||
/// </summary>
|
||||
protected abstract IPresenter[] Children { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Same as Start but called before children initialized, it's chance for propagate argument to children.
|
||||
/// </summary>
|
||||
protected abstract void BeforeInitialize(T argument);
|
||||
|
||||
/// <summary>
|
||||
/// Same as Start but called after all children are initialized.
|
||||
/// </summary>
|
||||
protected abstract void Initialize(T argument);
|
||||
|
||||
/// <summary>
|
||||
/// Force Start BeforeInitialize/Initialize. If you create presenter dynamically, maybe useful.
|
||||
/// </summary>
|
||||
public virtual void ForceInitialize(T argument)
|
||||
{
|
||||
Awake();
|
||||
PropagateArgument(argument);
|
||||
Start();
|
||||
}
|
||||
|
||||
void IPresenter.ForceInitialize(object argument)
|
||||
{
|
||||
ForceInitialize((T)argument);
|
||||
}
|
||||
|
||||
void IPresenter.Awake()
|
||||
{
|
||||
if (isAwaken) return;
|
||||
isAwaken = true;
|
||||
|
||||
children = Children;
|
||||
childrenCount = children.Length;
|
||||
|
||||
for (int i = 0; i < children.Length; i++)
|
||||
{
|
||||
var child = children[i];
|
||||
child.RegisterParent(this);
|
||||
child.Awake(); // call Awake directly
|
||||
}
|
||||
OnAwake();
|
||||
}
|
||||
|
||||
/// <summary>Infrastructure method called by UnityEngine. If you needs override Awake, override OnAwake.</summary>
|
||||
protected void Awake()
|
||||
{
|
||||
(this as IPresenter).Awake();
|
||||
}
|
||||
|
||||
/// <summary>An alternative of Awake.</summary>
|
||||
protected virtual void OnAwake()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Infrastructure method called by UnityEngine. don't call directly, don't override, don't hide!</summary>
|
||||
protected void Start()
|
||||
{
|
||||
if (isStartedCapturePhase) return;
|
||||
var root = parent;
|
||||
|
||||
// Search root object
|
||||
if (root == null)
|
||||
{
|
||||
root = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (root.Parent != null)
|
||||
{
|
||||
root = root.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
root.StartCapturePhase();
|
||||
}
|
||||
|
||||
void IPresenter.StartCapturePhase()
|
||||
{
|
||||
isStartedCapturePhase = true;
|
||||
BeforeInitialize(argument);
|
||||
|
||||
for (int i = 0; i < children.Length; i++)
|
||||
{
|
||||
var child = children[i];
|
||||
child.StartCapturePhase();
|
||||
}
|
||||
|
||||
// Start Bubbling phase
|
||||
if (children.Length == 0)
|
||||
{
|
||||
Initialize(argument);
|
||||
isInitialized = true;
|
||||
if (initializeSubject != null) { initializeSubject.OnNext(Unit.Default); initializeSubject.OnCompleted(); }
|
||||
if (parent != null)
|
||||
{
|
||||
parent.InitializeCore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IPresenter.RegisterParent(IPresenter parent)
|
||||
{
|
||||
if (this.parent != null) throw new InvalidOperationException("PresenterBase can't register multiple parent. Name:" + this.name);
|
||||
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
void IPresenter.InitializeCore()
|
||||
{
|
||||
currentCalledCount += 1;
|
||||
if (childrenCount == currentCalledCount)
|
||||
{
|
||||
Initialize(argument);
|
||||
isInitialized = true;
|
||||
if (initializeSubject != null) { initializeSubject.OnNext(Unit.Default); initializeSubject.OnCompleted(); }
|
||||
if (parent != null)
|
||||
{
|
||||
parent.InitializeCore();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 57bffdb091ac4164d850493fc89e9d6c
|
||||
timeCreated: 1455373899
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,332 @@
|
|||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2e22185fb1dbcef42bc613efd4769011
|
||||
timeCreated: 1455373898
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,408 @@
|
|||
using System;
|
||||
|
||||
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<Unit>
|
||||
/// </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 = canExecute.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);
|
||||
}
|
||||
|
||||
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 !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 RaectiveCommand to button's interactable and onClick.
|
||||
/// </summary>
|
||||
public static IDisposable BindTo(this ReactiveCommand<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 RaectiveCommand to button's interactable and onClick and register onClick action to command.
|
||||
/// </summary>
|
||||
public static IDisposable BindToOnClick(this ReactiveCommand<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 !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 AsyncReactiveCommand<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 AsyncReactiveCommand<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
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 939b249fde5252f45a4404e7648931ed
|
||||
timeCreated: 1462927720
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,511 @@
|
|||
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();
|
||||
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()
|
||||
{
|
||||
if (isDisposed) return Observable.Empty<int>();
|
||||
return countChanged ?? (countChanged = new Subject<int>());
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 12cd1079b0fe33f429f9f174c1f849af
|
||||
timeCreated: 1455373897
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,614 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if !UniRxLibrary
|
||||
using UnityEngine;
|
||||
#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; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight property broker.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ReactiveProperty<T> : IReactiveProperty<T>, IDisposable, IOptimizedObservable<T>
|
||||
{
|
||||
#if !UniRxLibrary
|
||||
static readonly IEqualityComparer<T> defaultEqualityComparer = UnityEqualityComparer.GetDefault<T>();
|
||||
#else
|
||||
static readonly IEqualityComparer<T> defaultEqualityComparer = EqualityComparer<T>.Default;
|
||||
#endif
|
||||
|
||||
[NonSerialized]
|
||||
bool canPublishValueOnSubscribe = false;
|
||||
|
||||
[NonSerialized]
|
||||
bool isDisposed = false;
|
||||
|
||||
#if !UniRxLibrary
|
||||
[SerializeField]
|
||||
#endif
|
||||
T value = default(T);
|
||||
|
||||
[NonSerialized]
|
||||
Subject<T> publisher = null;
|
||||
|
||||
[NonSerialized]
|
||||
IDisposable sourceConnection = null;
|
||||
|
||||
[NonSerialized]
|
||||
Exception lastException = null;
|
||||
|
||||
protected virtual IEqualityComparer<T> EqualityComparer
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultEqualityComparer;
|
||||
}
|
||||
}
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (!canPublishValueOnSubscribe)
|
||||
{
|
||||
canPublishValueOnSubscribe = true;
|
||||
SetValue(value);
|
||||
|
||||
if (isDisposed) return; // don't notify but set value
|
||||
var p = publisher;
|
||||
if (p != null)
|
||||
{
|
||||
p.OnNext(this.value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EqualityComparer.Equals(this.value, value))
|
||||
{
|
||||
SetValue(value);
|
||||
|
||||
if (isDisposed) return;
|
||||
var p = publisher;
|
||||
if (p != null)
|
||||
{
|
||||
p.OnNext(this.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return canPublishValueOnSubscribe;
|
||||
}
|
||||
}
|
||||
|
||||
public ReactiveProperty()
|
||||
: this(default(T))
|
||||
{
|
||||
// default constructor 'can' publish value on subscribe.
|
||||
// because sometimes value is deserialized from UnityEngine.
|
||||
}
|
||||
|
||||
public ReactiveProperty(T initialValue)
|
||||
{
|
||||
SetValue(initialValue);
|
||||
canPublishValueOnSubscribe = true;
|
||||
}
|
||||
|
||||
public ReactiveProperty(IObservable<T> source)
|
||||
{
|
||||
// initialized from source's ReactiveProperty `doesn't` publish value on subscribe.
|
||||
// because there ReactiveProeprty is `Future/Task/Promise`.
|
||||
|
||||
canPublishValueOnSubscribe = false;
|
||||
sourceConnection = source.Subscribe(new ReactivePropertyObserver(this));
|
||||
}
|
||||
|
||||
public ReactiveProperty(IObservable<T> source, T initialValue)
|
||||
{
|
||||
canPublishValueOnSubscribe = false;
|
||||
Value = initialValue; // Value set canPublishValueOnSubcribe = true
|
||||
sourceConnection = source.Subscribe(new ReactivePropertyObserver(this));
|
||||
}
|
||||
|
||||
protected virtual void SetValue(T value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void SetValueAndForceNotify(T value)
|
||||
{
|
||||
SetValue(value);
|
||||
|
||||
if (isDisposed) return;
|
||||
|
||||
var p = publisher;
|
||||
if (p != null)
|
||||
{
|
||||
p.OnNext(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<T> observer)
|
||||
{
|
||||
if (lastException != null)
|
||||
{
|
||||
observer.OnError(lastException);
|
||||
return Disposable.Empty;
|
||||
}
|
||||
|
||||
if (isDisposed)
|
||||
{
|
||||
observer.OnCompleted();
|
||||
return Disposable.Empty;
|
||||
}
|
||||
|
||||
if (publisher == null)
|
||||
{
|
||||
// Interlocked.CompareExchange is bit slower, guarantee threasafety is overkill.
|
||||
// System.Threading.Interlocked.CompareExchange(ref publisher, new Subject<T>(), null);
|
||||
publisher = new Subject<T>();
|
||||
}
|
||||
|
||||
var p = publisher;
|
||||
if (p != null)
|
||||
{
|
||||
var subscription = p.Subscribe(observer);
|
||||
if (canPublishValueOnSubscribe)
|
||||
{
|
||||
observer.OnNext(value); // raise latest value on subscribe
|
||||
}
|
||||
return subscription;
|
||||
}
|
||||
else
|
||||
{
|
||||
observer.OnCompleted();
|
||||
return Disposable.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!isDisposed)
|
||||
{
|
||||
isDisposed = true;
|
||||
var sc = sourceConnection;
|
||||
if (sc != null)
|
||||
{
|
||||
sc.Dispose();
|
||||
sourceConnection = null;
|
||||
}
|
||||
var p = publisher;
|
||||
if (p != null)
|
||||
{
|
||||
// when dispose, notify OnCompleted
|
||||
try
|
||||
{
|
||||
p.OnCompleted();
|
||||
}
|
||||
finally
|
||||
{
|
||||
p.Dispose();
|
||||
publisher = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (value == null) ? "(null)" : value.ToString();
|
||||
}
|
||||
|
||||
public bool IsRequiredSubscribeOnCurrentThread()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
class ReactivePropertyObserver : IObserver<T>
|
||||
{
|
||||
readonly ReactiveProperty<T> parent;
|
||||
int isStopped = 0;
|
||||
|
||||
public ReactivePropertyObserver(ReactiveProperty<T> parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
parent.Value = value;
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
if (System.Threading.Interlocked.Increment(ref isStopped) == 1)
|
||||
{
|
||||
parent.lastException = error;
|
||||
var p = parent.publisher;
|
||||
if (p != null)
|
||||
{
|
||||
p.OnError(error);
|
||||
}
|
||||
parent.Dispose(); // complete subscription
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
if (System.Threading.Interlocked.Increment(ref isStopped) == 1)
|
||||
{
|
||||
// source was completed but can publish from .Value yet.
|
||||
var sc = parent.sourceConnection;
|
||||
parent.sourceConnection = null;
|
||||
if (sc != null)
|
||||
{
|
||||
sc.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight property broker.
|
||||
/// </summary>
|
||||
public class ReadOnlyReactiveProperty<T> : IReadOnlyReactiveProperty<T>, IDisposable, IOptimizedObservable<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;
|
||||
|
||||
Exception lastException = null;
|
||||
|
||||
T value = default(T);
|
||||
|
||||
Subject<T> publisher = null;
|
||||
|
||||
IDisposable sourceConnection = null;
|
||||
|
||||
bool isSourceCompleted = false;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return canPublishValueOnSubscribe;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IEqualityComparer<T> EqualityComparer
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultEqualityComparer;
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlyReactiveProperty(IObservable<T> source)
|
||||
{
|
||||
this.sourceConnection = source.Subscribe(new ReadOnlyReactivePropertyObserver(this));
|
||||
}
|
||||
|
||||
public ReadOnlyReactiveProperty(IObservable<T> source, bool distinctUntilChanged)
|
||||
{
|
||||
this.distinctUntilChanged = distinctUntilChanged;
|
||||
this.sourceConnection = source.Subscribe(new ReadOnlyReactivePropertyObserver(this));
|
||||
}
|
||||
|
||||
public ReadOnlyReactiveProperty(IObservable<T> source, T initialValue)
|
||||
{
|
||||
this.value = initialValue;
|
||||
this.canPublishValueOnSubscribe = true;
|
||||
this.sourceConnection = source.Subscribe(new ReadOnlyReactivePropertyObserver(this));
|
||||
}
|
||||
|
||||
public ReadOnlyReactiveProperty(IObservable<T> source, T initialValue, bool distinctUntilChanged)
|
||||
{
|
||||
this.distinctUntilChanged = distinctUntilChanged;
|
||||
this.value = initialValue;
|
||||
this.canPublishValueOnSubscribe = true;
|
||||
this.sourceConnection = source.Subscribe(new ReadOnlyReactivePropertyObserver(this));
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<T> observer)
|
||||
{
|
||||
if (lastException != null)
|
||||
{
|
||||
observer.OnError(lastException);
|
||||
return Disposable.Empty;
|
||||
}
|
||||
|
||||
if (isDisposed)
|
||||
{
|
||||
observer.OnCompleted();
|
||||
return Disposable.Empty;
|
||||
}
|
||||
|
||||
if (isSourceCompleted)
|
||||
{
|
||||
if (canPublishValueOnSubscribe)
|
||||
{
|
||||
observer.OnNext(value);
|
||||
observer.OnCompleted();
|
||||
return Disposable.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
observer.OnCompleted();
|
||||
return Disposable.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (publisher == null)
|
||||
{
|
||||
// Interlocked.CompareExchange is bit slower, guarantee threasafety is overkill.
|
||||
// System.Threading.Interlocked.CompareExchange(ref publisher, new Subject<T>(), null);
|
||||
publisher = new Subject<T>();
|
||||
}
|
||||
|
||||
var p = publisher;
|
||||
if (p != null)
|
||||
{
|
||||
var subscription = p.Subscribe(observer);
|
||||
if (canPublishValueOnSubscribe)
|
||||
{
|
||||
observer.OnNext(value); // raise latest value on subscribe
|
||||
}
|
||||
return subscription;
|
||||
}
|
||||
else
|
||||
{
|
||||
observer.OnCompleted();
|
||||
return Disposable.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!isDisposed)
|
||||
{
|
||||
isDisposed = true;
|
||||
var sc = sourceConnection;
|
||||
if (sc != null)
|
||||
{
|
||||
sc.Dispose();
|
||||
sourceConnection = null;
|
||||
}
|
||||
|
||||
var p = publisher;
|
||||
if (p != null)
|
||||
{
|
||||
// when dispose, notify OnCompleted
|
||||
try
|
||||
{
|
||||
p.OnCompleted();
|
||||
}
|
||||
finally
|
||||
{
|
||||
p.Dispose();
|
||||
publisher = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (value == null) ? "(null)" : value.ToString();
|
||||
}
|
||||
|
||||
public bool IsRequiredSubscribeOnCurrentThread()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
class ReadOnlyReactivePropertyObserver : IObserver<T>
|
||||
{
|
||||
readonly ReadOnlyReactiveProperty<T> parent;
|
||||
int isStopped = 0;
|
||||
|
||||
public ReadOnlyReactivePropertyObserver(ReadOnlyReactiveProperty<T> parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
if (parent.distinctUntilChanged && parent.canPublishValueOnSubscribe)
|
||||
{
|
||||
if (!parent.EqualityComparer.Equals(parent.value, value))
|
||||
{
|
||||
parent.value = value;
|
||||
var p = parent.publisher;
|
||||
if (p != null)
|
||||
{
|
||||
p.OnNext(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.value = value;
|
||||
parent.canPublishValueOnSubscribe = true;
|
||||
|
||||
var p = parent.publisher;
|
||||
if (p != null)
|
||||
{
|
||||
p.OnNext(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
if (System.Threading.Interlocked.Increment(ref isStopped) == 1)
|
||||
{
|
||||
parent.lastException = error;
|
||||
var p = parent.publisher;
|
||||
if (p != null)
|
||||
{
|
||||
p.OnError(error);
|
||||
}
|
||||
parent.Dispose(); // complete subscription
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
if (System.Threading.Interlocked.Increment(ref isStopped) == 1)
|
||||
{
|
||||
parent.isSourceCompleted = true;
|
||||
var sc = parent.sourceConnection;
|
||||
parent.sourceConnection = null;
|
||||
if (sc != null)
|
||||
{
|
||||
sc.Dispose();
|
||||
}
|
||||
|
||||
var p = parent.publisher;
|
||||
parent.publisher = null;
|
||||
if (p != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
p.OnCompleted();
|
||||
}
|
||||
finally
|
||||
{
|
||||
p.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods of ReactiveProperty<T>
|
||||
/// </summary>
|
||||
public static class ReactivePropertyExtensions
|
||||
{
|
||||
public static ReactiveProperty<T> ToReactiveProperty<T>(this IObservable<T> source)
|
||||
{
|
||||
return new ReactiveProperty<T>(source);
|
||||
}
|
||||
|
||||
public static ReactiveProperty<T> ToReactiveProperty<T>(this IObservable<T> source, T initialValue)
|
||||
{
|
||||
return new ReactiveProperty<T>(source, initialValue);
|
||||
}
|
||||
|
||||
public static ReadOnlyReactiveProperty<T> ToReadOnlyReactiveProperty<T>(this IObservable<T> source)
|
||||
{
|
||||
return new ReadOnlyReactiveProperty<T>(source);
|
||||
}
|
||||
|
||||
/// <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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 88e12aa895fef434fbe3ea0cc8f57301
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#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()
|
||||
{
|
||||
EditorApplication.playModeStateChanged += (state) =>
|
||||
{
|
||||
// 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
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8d380d86e2ef6674c83ca983a1604273
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/Toolkit.meta
Normal file
10
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/Toolkit.meta
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dfd9ce3144f934344bebf3bb8f92aa0f
|
||||
folderAsset: yes
|
||||
timeCreated: 1511817513
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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 disposedValue = 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 (disposedValue) 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 (disposedValue) 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(_ => disposedValue)
|
||||
.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 (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Clear(false);
|
||||
}
|
||||
|
||||
disposedValue = 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 disposedValue = 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 (disposedValue) 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 (disposedValue) 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(_ => disposedValue)
|
||||
.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 (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Clear(false);
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f4980e1e001c7e94fab3250ba284dc91
|
||||
timeCreated: 1468655394
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/Triggers.meta
Normal file
10
Assets/Plugins/UniRx/Scripts/UnityEngineBridge/Triggers.meta
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 615eb5e6faff3874eb2d640203075774
|
||||
folderAsset: yes
|
||||
timeCreated: 1511817513
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e03f9257cc6667f4082439aa77d6f01e
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// 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>());
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
onBeginDrag = null;
|
||||
}
|
||||
|
||||
protected override void RaiseOnCompletedOnDestroy()
|
||||
{
|
||||
if (onBeginDrag != null)
|
||||
{
|
||||
onBeginDrag.OnCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3a81a9b6bec6b4f4fba7e0047cd989f6
|
||||
timeCreated: 1455373898
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4c0a9070b7cc23746b2c0e2db3ec16cd
|
||||
timeCreated: 1455373898
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 54095d3e740f7714085d0568207cbfe0
|
||||
timeCreated: 1455373899
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1be7847b61f30f24daa5762db87a5b19
|
||||
timeCreated: 1455373897
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 10b917196cbfcf74898ce1686e205d04
|
||||
timeCreated: 1455373897
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9fe6f69c4d869c04e8a1924aab1d3694
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cb219b23cdf4b314f94a27bca3cc8012
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// 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>());
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
onDrag = null;
|
||||
}
|
||||
|
||||
protected override void RaiseOnCompletedOnDestroy()
|
||||
{
|
||||
if (onDrag != null)
|
||||
{
|
||||
onDrag.OnCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 79db090dc9e4db245821e8b89b0e208e
|
||||
timeCreated: 1455373899
|
||||
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
Loading…
Reference in a new issue