Esempio n. 1
0
        //public virtual async UniTask NavigateAsync(ISceneArgs args, IProgress<float> progress = null)
        //{
        //    // 結果を待ってるシーンがあるならダメ
        //    if (this._taskCompletionSourcesByResultRequirementId.Count > 0)
        //    {
        //        throw new NavigationFailureException("結果を待っているシーンがあります", args);
        //    }

        //    for(var i = 0; i < args.SubScenes.Count; ++i)
        //    {
        //        if (args.SubScenes[i].SceneName)
        //        {

        //        }
        //    }

        //    await NavigateCoreAsync(args, NavigationOption.Push, progress);
        //}

        public virtual async UniTask <TResult> NavigateNextAsync <TResult>(ISceneArgs args, IProgress <float> progress = null)
        {
            using (var internalProgresses = new NavigationInternalProgressGroup(progress, 1))
            {
                var resultRequirementId  = Guid.NewGuid();
                var taskCompletionSource = new UniTaskCompletionSource <object>();
                if (this._taskCompletionSourcesByResultRequirementId.ContainsKey(resultRequirementId))
                {
                    throw new NavigationFailureException("シーンをテーブルに追加できませんでした", args);
                }
                this._taskCompletionSourcesByResultRequirementId[resultRequirementId] = taskCompletionSource;

                var activateResult = await NavigateCoreAsync(args, NavigationOption.Popup, internalProgresses[0]);

                // ここでダメな場合は既にActivateAsyncでエラーを吐いてるハズ
                if (activateResult == null)
                {
                    return(default(TResult));
                }

                activateResult.NextScene.ResultRequirementId = resultRequirementId;

                var result = await taskCompletionSource.Task;

                if (!(result is TResult))
                {
                    throw new NavigationFailureException($"戻り値の型は{typeof(TResult)}を期待しましたが、{result.GetType()}が返されました", args);
                }

                return((TResult)result);
            }
        }
Esempio n. 2
0
            public static NavigationLock Acquire(ISceneArgs args)
            {
                if (_lock != null)
                {
                    throw new NavigationFailureException("前回の遷移が終了する前に新しい遷移が開始されました", args);
                }

                return(_lock = new NavigationLock());
            }
Esempio n. 3
0
        private async UniTask UnloadAsync(ISceneArgs args, CancellationToken token, IProgress <float> progress = null)
        {
            var asyncOperation = SceneManager.UnloadSceneAsync(args.SceneName);

            progress?.Report(0f);
            while (!asyncOperation.isDone)
            {
                progress?.Report(asyncOperation.progress);
                await UniTask.Delay(TimeSpan.FromSeconds(Time.fixedDeltaTime));

                if (token.IsCancellationRequested)
                {
                    throw new OperationCanceledException("遷移処理がキャンセルされました", new NavigationFailureException("遷移処理がキャンセルされました", args), token);
                }
            }
            progress?.Report(1f);

            if (!this._scenesByName.Remove(args.SceneName))
            {
                throw new NavigationFailureException("シーンをテーブルから削除できませんでした", args);
            }
        }
Esempio n. 4
0
        private async UniTask <NavigationResult> LoadAsync(ISceneArgs args, CancellationToken token, NavigationOption option = NavigationOption.None, IProgress <float> progress = null)
        {
            var asyncOperation = SceneManager.LoadSceneAsync(args.SceneName, LoadSceneMode.Additive);

            progress?.Report(0f);
            while (!asyncOperation.isDone)
            {
                progress?.Report(asyncOperation.progress);
                await UniTask.Delay(TimeSpan.FromSeconds(Time.fixedDeltaTime));

                if (token.IsCancellationRequested)
                {
                    throw new OperationCanceledException("遷移処理がキャンセルされました", new NavigationFailureException("遷移処理がキャンセルされました", args), token);
                }
            }
            progress?.Report(1f);

            var result = Activate(args, option);

            // ここに来たという事は新規
            result.TransitionMode |= TransitionMode.New;

            if (this._scenesByName.ContainsKey(args.SceneName))
            {
                throw new NavigationFailureException("シーンを重複して読み込もうとしています", args);
            }
            this._scenesByName[args.SceneName] = result.NextScene;

            // ロード時にCanvasの調整をする
            if (this._canvasCustomizer != null)
            {
                this._canvasCustomizer.Customize(result.NextScene.RootCanvases);
            }

            return(result);
        }
Esempio n. 5
0
 public virtual UniTask ResetAsync(ISceneArgs args, TransitionMode mode, IProgress <float> progress) => UniTask.CompletedTask;
Esempio n. 6
0
 void INavigatableScene.SetParentSceneArgs(ISceneArgs args)
 {
     this.ParentSceneArgs = args;
 }
Esempio n. 7
0
        private NavigationResult Activate(ISceneArgs args, NavigationOption option = NavigationOption.None)
        {
            var result = new NavigationResult();

            if (this._currentScene != null)
            {
                var currentUnityScene = SceneManager.GetSceneByName(this._currentScene.SceneArgs.SceneName);
                if (!currentUnityScene.isLoaded)
                {
                    throw new NavigationFailureException("無効なシーンが設定されています", args);
                }

                result.PreviousScene = this._currentScene;
            }

            // シーンマネージャの方から次のSceneを取得
            var nextUnityScene = SceneManager.GetSceneByName(args.SceneName);

            if (!nextUnityScene.isLoaded)
            {
                throw new NavigationFailureException("シーンの読み込みに失敗しました", args);
            }
            if (nextUnityScene.rootCount != 1)
            {
                throw new NavigationFailureException("シーンのRootObjectが複数あります", args);
            }

            // SceneからINavigatableSceneを取得
            var rootObjects = nextUnityScene.GetRootGameObjects();

            if (rootObjects.Length == 0)
            {
                throw new NavigationFailureException("RootObjectが存在しません", args);
            }
            if (rootObjects.Length > 1)
            {
                throw new NavigationFailureException("RootObjectが複数あります", args);
            }

            var containsCanvases = rootObjects[0].GetComponentsInChildren <Canvas>();

            if (containsCanvases.Length == 0)
            {
                throw new NavigationFailureException("Canvasが見つかりませんでした", args);
            }

            var sceneBases = rootObjects[0].GetComponents <SceneBase>();

            if (sceneBases.Length == 0)
            {
                throw new NavigationFailureException("SceneBaseコンポーネントがRootObjectに存在しません", args);
            }
            if (sceneBases.Length > 1)
            {
                throw new NavigationFailureException("SceneBaseコンポーネントが複数あります", args);
            }

            // 進む場合、新しいシーンは非表示にしておく
            if (!option.HasFlag(NavigationOption.Pop))
            {
                rootObjects[0].SetActive(false);
            }

            // 次のシーンに諸々引数を渡す
            var nextScene = sceneBases[0] as INavigatableScene;

            nextScene.SceneArgs = args;
            nextScene.SetNavigator(this);
            if (this._currentScene != null)
            {
                nextScene.SetParentSceneArgs(this._currentScene.SceneArgs);
            }

            // 進む場合、ソートを整える
            if (!option.HasFlag(NavigationOption.Pop))
            {
                if (this._currentScene != null)
                {
                    this._canvasOrderArranger.ArrangeOrder(this._currentScene.RootCanvases, option);
                }
                else
                {
                    for (var i = 0; i < nextScene.RootCanvases.Count; ++i)
                    {
                        nextScene.RootCanvases[i].sortingOrder = this._canvasOrderArranger.InitialOrder;
                    }
                }
            }

            // 次のシーンにnextSceneを設定
            this._currentScene = result.NextScene = nextScene;

            // TransitionModeの調整
            if (option.HasFlag(NavigationOption.Override))
            {
                result.TransitionMode |= TransitionMode.KeepCurrent;
            }
            if (option.HasFlag(NavigationOption.Pop))
            {
                result.TransitionMode |= TransitionMode.Back;
            }

            return(result);
        }
Esempio n. 8
0
        private async UniTask <SubSceneTransitions> NavigateSubScenesCoreAsync(ISceneArgs args, CancellationToken token, IProgress <float> progress = null)
        {
            var result = new SubSceneTransitions();

            if (!args.SubScenes.Any())
            {
                return(result);
            }

            var internalProgresses = new NavigationInternalProgressGroup(progress, 2);

            var removes = this._currentSubScenesByName
                          .Where(x => args.SubScenes.All(y => y.SceneName != x.Key))
                          .Select(x => x.Value)
                          .ToArray();

            using (var internalDisactivateProgresses = new NavigationInternalProgressGroup(internalProgresses[0], removes.Length))
            {
                for (var i = 0; i < removes.Length; ++i)
                {
                    var disactivationResult = new NavigationResult()
                    {
                        PreviousScene  = removes[i],
                        TransitionMode = TransitionMode.Back,
                    };

                    await removes[i].LeaveAsync(disactivationResult.TransitionMode, internalDisactivateProgresses[i]);
                    internalDisactivateProgresses[i].Report(1f);

                    // 古いシーンの遷移処理を呼ぶ
                    result.OnLeftTasks.Add(async(CancellationToken innerToken, IProgress <float> innerProgress) =>
                    {
                        await this._afterTransition.OnLeftAsync(disactivationResult, innerToken, innerProgress);

                        // シーンのファイナライズ処理
                        disactivationResult.PreviousScene.Collapse();

                        await UnloadAsync(disactivationResult.PreviousScene.SceneArgs, token);
                    });
                }
            }

            using (var internalActivateProgresses = new NavigationInternalProgressGroup(internalProgresses[1], args.SubScenes.Count))
            {
                for (var i = 0; i < args.SubScenes.Count; ++i)
                {
                    using (var internalActivateProgresses2 = new NavigationInternalProgressGroup(internalActivateProgresses[i], 3))
                    {
                        var subSceneArgs = args.SubScenes[i];
                        if (this._scenesByName.ContainsKey(subSceneArgs.SceneName))
                        {
                            throw new NavigationFailureException($"{subSceneArgs.SceneName}は既に通常シーンとしてロードされています。通常シーンをサブシーンに変更することはできません。", args);
                        }

                        NavigationResult activationResult;
                        if (this._currentSubScenesByName.ContainsKey(args.SubScenes[i].SceneName))
                        {
                            // 既にInitialize済みのSceneであればActivateするだけでOK
                            activationResult = this.Activate(subSceneArgs, NavigationOption.Sub);
                        }
                        else
                        {
                            activationResult = await this.LoadAsync(subSceneArgs, token, NavigationOption.Sub, internalActivateProgresses2[0]);
                        }

                        internalActivateProgresses2[i].Report(1f);

                        // 新しいシーンをリセットする
                        await activationResult.NextScene.ResetAsync(args, activationResult.TransitionMode, internalActivateProgresses2[1]);

                        internalActivateProgresses2[1].Report(1f);

                        if (activationResult.NextScene.SceneLifeCancellationToken.IsCancellationRequested)
                        {
                            throw new OperationCanceledException("遷移処理がキャンセルされました", new NavigationFailureException("遷移処理がキャンセルされました", args), activationResult.NextScene.SceneLifeCancellationToken);
                        }

                        // 新規シーンなら初期化する
                        if (activationResult.TransitionMode.HasFlag(TransitionMode.New))
                        {
                            activationResult.NextScene.Initialize();
                        }

                        // 新規シーンに入る
                        await activationResult.NextScene.EnterAsync(activationResult.TransitionMode, internalActivateProgresses2[2]);

                        internalActivateProgresses2[2].Report(1f);

                        // 新規シーンに入ったら外部の遷移処理を呼ぶ
                        result.OnEnteredTasks.Add(async(CancellationToken innerToken, IProgress <float> innerProgress) =>
                        {
                            for (var j = 0; j < activationResult.NextScene.RootCanvases.Count; ++j)
                            {
                                activationResult.NextScene.RootCanvases[j].enabled = false;
                            }
                            activationResult.NextScene.RootObject.SetActive(true);

                            await this._afterTransition.OnEnteredAsync(activationResult, activationResult.NextScene.SceneLifeCancellationToken, new Progress <float>());
                            if (activationResult.NextScene.SceneLifeCancellationToken.IsCancellationRequested)
                            {
                                throw new OperationCanceledException("遷移処理がキャンセルされました", new NavigationFailureException("遷移処理がキャンセルされました", args), activationResult.NextScene.SceneLifeCancellationToken);
                            }

                            for (var j = 0; j < activationResult.NextScene.RootCanvases.Count; ++j)
                            {
                                activationResult.NextScene.RootCanvases[j].enabled = true;
                            }
                        });
                    }
                }
            }

            return(result);
        }
Esempio n. 9
0
        private async UniTask <NavigationResult> NavigateCoreAsync(ISceneArgs args, NavigationOption option = NavigationOption.None, IProgress <float> progress = null)
        {
            if (this._currentSubScenesByName.ContainsKey(args.SceneName))
            {
                throw new NavigationFailureException($"{args.SceneName}は既にサブシーンとしてロードされています。サブシーンを通常シーンに変更することはできません。", args);
            }

            using (var internalProgresses = new NavigationInternalProgressGroup(progress, 6))
                using (NavigationLock.Acquire(args))
                {
                    var loadingDisplay = default(ILoadingDisplay);

                    if (this._loadingDisplaySelector != null)
                    {
                        if (this._loadingDisplaysByOption.ContainsKey((int)option))
                        {
                            loadingDisplay = this._loadingDisplaysByOption[(int)option];
                        }
                        else
                        {
                            loadingDisplay = this._loadingDisplaysByOption[(int)option] = this._loadingDisplaySelector.SelectDisplay(option);
                        }
                    }

                    if (loadingDisplay != null)
                    {
                        loadingDisplay.Show();
                    }

                    var cancellationToken = this._currentScene?.SceneLifeCancellationToken ?? new CancellationTokenSource().Token;

                    // まずサブシーンを処理する
                    var subSceneTransitions = await NavigateSubScenesCoreAsync(args, cancellationToken, internalProgresses[0]);

                    internalProgresses[0].Report(1f);

                    NavigationResult activationResult;
                    if (this._scenesByName.ContainsKey(args.SceneName))
                    {
                        // 既にInitialize済みのSceneであればActivateするだけでOK
                        activationResult = this.Activate(args, option);
                    }
                    else
                    {
                        activationResult = await this.LoadAsync(args, cancellationToken, option, internalProgresses[1]);
                    }
                    internalProgresses[1].Report(1f);

                    // ここでダメな場合は既にActivateAsyncでエラーを吐いてるハズ
                    if (activationResult == null || activationResult.NextScene == null)
                    {
                        return(null);
                    }

                    if (option.HasFlag(NavigationOption.Push))
                    {
                        // 新しいシーンをスタックに積む
                        this._navigateHistoryStack.Push(new NavigationStackElement()
                        {
                            SceneName = args.SceneName, TransitionMode = activationResult.TransitionMode
                        });
                    }

                    // 新しいシーンをリセットする
                    await activationResult.NextScene.ResetAsync(args, activationResult.TransitionMode, internalProgresses[2]);

                    internalProgresses[2].Report(1f);

                    if (activationResult.NextScene.SceneLifeCancellationToken.IsCancellationRequested)
                    {
                        throw new OperationCanceledException("遷移処理がキャンセルされました", new NavigationFailureException("遷移処理がキャンセルされました", args), activationResult.NextScene.SceneLifeCancellationToken);
                    }

                    // 新規シーンなら初期化する
                    if (activationResult.TransitionMode.HasFlag(TransitionMode.New))
                    {
                        activationResult.NextScene.Initialize();
                    }

                    // 新規シーンに入る
                    await activationResult.NextScene.EnterAsync(activationResult.TransitionMode, internalProgresses[3]);

                    internalProgresses[3].Report(1f);
                    if (activationResult.NextScene.SceneLifeCancellationToken.IsCancellationRequested)
                    {
                        throw new OperationCanceledException("遷移処理がキャンセルされました", new NavigationFailureException("遷移処理がキャンセルされました", args), activationResult.NextScene.SceneLifeCancellationToken);
                    }

                    // 古いシーンから出る
                    if (activationResult.PreviousScene != null)
                    {
                        await activationResult.PreviousScene.LeaveAsync(activationResult.TransitionMode, internalProgresses[4]);

                        internalProgresses[4].Report(1f);
                    }

                    // 新規シーンに入ったら外部の遷移処理を呼ぶ
                    async UniTask enterNext()
                    {
                        for (var i = 0; i < activationResult.NextScene.RootCanvases.Count; ++i)
                        {
                            activationResult.NextScene.RootCanvases[i].enabled = false;
                        }
                        activationResult.NextScene.RootObject.SetActive(true);

                        await this._afterTransition.OnEnteredAsync(activationResult, activationResult.NextScene.SceneLifeCancellationToken, new Progress <float>());

                        if (activationResult.NextScene.SceneLifeCancellationToken.IsCancellationRequested)
                        {
                            throw new OperationCanceledException("遷移処理がキャンセルされました", new NavigationFailureException("遷移処理がキャンセルされました", args), activationResult.NextScene.SceneLifeCancellationToken);
                        }

                        for (var i = 0; i < activationResult.NextScene.RootCanvases.Count; ++i)
                        {
                            activationResult.NextScene.RootCanvases[i].enabled = true;
                        }
                    }

                    // 古いシーンの遷移処理を呼ぶ
                    async UniTask leavePrevious()
                    {
                        if (activationResult.PreviousScene != null)
                        {
                            await this._afterTransition.OnLeftAsync(activationResult, activationResult.NextScene.SceneLifeCancellationToken, new Progress <float>());

                            if (activationResult.NextScene.SceneLifeCancellationToken.IsCancellationRequested)
                            {
                                throw new OperationCanceledException("遷移処理がキャンセルされました", new NavigationFailureException("遷移処理がキャンセルされました", args), activationResult.NextScene.SceneLifeCancellationToken);
                            }

                            // 上に乗せるフラグが無ければ非アクティブ化
                            if (!option.HasFlag(NavigationOption.Override))
                            {
                                activationResult.PreviousScene.RootObject.SetActive(false);
                            }

                            // Popするならアンロードも行う
                            if (option.HasFlag(NavigationOption.Pop))
                            {
                                // シーンのファイナライズ処理
                                activationResult.PreviousScene.Collapse();

                                // 古いシーンをスタックから抜いてアンロード
                                var popObject = this._navigateHistoryStack.Pop();
                                await UnloadAsync(activationResult.PreviousScene.SceneArgs, activationResult.NextScene.SceneLifeCancellationToken, internalProgresses[5]);

                                if (activationResult.NextScene.SceneLifeCancellationToken.IsCancellationRequested)
                                {
                                    throw new OperationCanceledException("遷移処理がキャンセルされました", new NavigationFailureException("遷移処理がキャンセルされました", args), activationResult.NextScene.SceneLifeCancellationToken);
                                }
                            }
                        }

                        internalProgresses[5].Report(1f);
                    }

                    // 遷移処理は同時に進行
                    await UniTask.WhenAll(
                        enterNext(),
                        leavePrevious(),
                        subSceneTransitions.OnEnteredAsync(activationResult.NextScene.SceneLifeCancellationToken),
                        subSceneTransitions.OnLeftAsync(activationResult.NextScene.SceneLifeCancellationToken));

                    if (activationResult.NextScene.SceneLifeCancellationToken.IsCancellationRequested)
                    {
                        throw new OperationCanceledException("遷移処理がキャンセルされました", new NavigationFailureException("遷移処理がキャンセルされました", args), activationResult.NextScene.SceneLifeCancellationToken);
                    }

                    if (loadingDisplay != null)
                    {
                        loadingDisplay.Hide();
                    }

                    return(activationResult);
                }
        }
Esempio n. 10
0
 public NavigationFailureException(string message, ISceneArgs args) : base(message + ": " + args?.SceneName)
 {
 }