private static IObservable <T> Tween <T>(Func <OperationalStructBase <T> > start, Func <OperationalStructBase <T> > finish, Func <float> duration, EaseType easeType, LoopType loopType, Action onCompleteTween, bool ignoreTimeScale) where T : struct { T startValue = default; T finishValue = default; onCompleteTween = onCompleteTween ?? (() => { }); IDisposable ReturnStartValue(IObserver <T> observer) { observer.OnNext(startValue); return(null); } IDisposable ReturnFinishValue(IObserver <T> observer) { observer.OnNext(finishValue); return(null); } IObservable <T> stream = Observable.Empty <TweenInformation <T> >() // Repeat() のために、毎回初期値を生成 .StartWith(() => new TweenInformation <T>(ignoreTimeScale ? Time.unscaledTime : Time.time, start(), finish(), duration(), easeType, out startValue, out finishValue)) // Update のストリームに変換 .SelectMany(information => Observable.Interval(TimeSpan.FromMilliseconds(1), ignoreTimeScale ? Scheduler.MainThreadIgnoreTimeScale : Scheduler.MainThread).Do(_ => information.Time = (ignoreTimeScale ? Time.unscaledTime : Time.time) - information.StartTime).Select(_ => information)) // Tween 時間が処理時間よりも小さい間流し続ける .TakeWhile(information => information.Time <= information.Duration) // 実際の Easing 処理実行 .Select(information => Easing(information.Time, information.Start, (information.Finish - information.Start), information.Duration, information.EaseType).Value) // 最終フレームの値を確実に流すために OnCompleted が来たら値を一つ流すストリームに繋ぐ // 1回分の Tween が終わったらコールバックを呼ぶ .Concat(Observable.Create((Func <IObserver <T>, IDisposable>)ReturnFinishValue).Take(1).Do(_ => onCompleteTween())); switch (loopType) { case LoopType.None: // Do nothing. break; case LoopType.Repeat: stream = stream.Repeat(); break; case LoopType.PingPong: stream = stream .Concat( Observable.Empty <TweenInformation <T> >() // Repeat() のために、毎回初期値を生成 .StartWith(() => new TweenInformation <T>(ignoreTimeScale ? Time.unscaledTime : Time.time, start(), finish(), duration(), easeType, out startValue, out finishValue)) // Update のストリームに変換 .SelectMany(information => Observable.Interval(TimeSpan.FromMilliseconds(1), ignoreTimeScale ? Scheduler.MainThreadIgnoreTimeScale : Scheduler.MainThread).Do(_ => information.Time = (ignoreTimeScale ? Time.unscaledTime : Time.time) - information.StartTime).Select(_ => information)) // Tween 時間が処理時間よりも小さい間流し続ける .TakeWhile(information => information.Time <= information.Duration) // start と finish を入れ替えて、実際の Easing 処理実行 .Select(information => Easing(information.Time, information.Finish, (information.Start - information.Finish), information.Duration, information.EaseType).Value) // 最終フレームの値を確実に流すために OnCompleted が来たら最終値を一つ流すストリームに繋ぐ // 1回分の Tween が終わったらコールバックを呼ぶ .Concat(Observable.Create((Func <IObserver <T>, IDisposable>)ReturnStartValue).Take(1).Do(_ => onCompleteTween())) ) .Repeat(); break; case LoopType.Mirror: stream = stream .Concat( Observable.Empty <TweenInformation <T> >() // Repeat() のために、毎回初期値を生成 .StartWith(() => new TweenInformation <T>(ignoreTimeScale ? Time.unscaledTime : Time.time, start(), finish(), duration(), easeType, out startValue, out finishValue)) // Update のストリームに変換 .SelectMany(information => Observable.Interval(TimeSpan.FromMilliseconds(1), ignoreTimeScale ? Scheduler.MainThreadIgnoreTimeScale : Scheduler.MainThread).Do(_ => information.Time = (ignoreTimeScale ? Time.unscaledTime : Time.time) - information.StartTime).Select(_ => information)) // Tween 時間が処理時間よりも小さい間流し続ける .TakeWhile(information => information.Time <= information.Duration) // start と finish を入れ替えて、実際の Easing 処理実行 .Select(information => Easing(information.Time, information.Finish, (information.Start - information.Finish), information.Duration, MirrorEaseTypeMap[information.EaseType]).Value) // 最終フレームの値を確実に流すために OnCompleted が来たら最終値を一つ流すストリームに繋ぐ // 1回分の Tween が終わったらコールバックを呼ぶ .Concat(Observable.Create((Func <IObserver <T>, IDisposable>)ReturnStartValue).Take(1).Do(_ => onCompleteTween())) ) .Repeat(); break; default: throw new ArgumentOutOfRangeException(nameof(loopType), loopType, null); } return(stream); }