メインコンテンツまでスキップ
バージョン: 1.3.0

Core

ここではCoreについて学習します。

  • 学習時間の目安
    • 60分
  • Unityバージョン
    • 2022.3.17f1

プロジェクトの作成からCoreの機能を導入していき、VContainerやUniRxも導入しExtrealが想定するアプリケーションアーキテクチャを構築します。

Create project

step

まずはプロジェクトを作りましょう。

Unity Hubから新しいプロジェクトを作成します。

プロジェクト作成

  • エディターバージョン: 2022.3.17f1
  • テンプレート: 3D
  • プロジェクト名: ExtrealCoreLearning
step

Unityエディタが起動したら実行して問題がないことを確認します。

SampleSceneを実行してみましょう。 シーンに何も配置していないので何も起こりません。 Consoleに何も出ていなければ成功です。

Create entry point

step

アプリケーション用のディレクトリとエントリーポイントとなるAppシーンを作ります。

Appシーン

  • 初期設定してあるAssets/Scenesディレクトリを削除します。
  • Assets/ExtrealCoreLearning/Appとなるディレクトリを作ります。
  • AppディレクトリにAppシーンを作成します。

Add Logging

Loggingをアプリケーションに追加します。

step

Package ManagerからLoggingを追加します。

Git URL指定でLoggingを追加します。 Git URLはLoggingのPackageから取得します。

Logging追加

Package ManagerにExtreal.Core.Loggingが追加されれば成功です。 バージョンは実施タイミングにより異なります。

step

スクリプトからLoggingを参照できるようにします。

アプリケーションのAssembly DefinitionにLoggingを設定します。

Loggingアセンブリ設定

  • ExtrealCoreLearningディレクトリにExtrealCoreLearningという名前のAssembly Definitionを作成します。
  • Root NamespaceにExtrealCoreLearningを指定します。
  • Assembly Definition ReferencesにExtreal.Core.Loggingを追加します。
  • Assembly Definition ReferencesのUse GUIDsのチェックは外します。
step

開発用にDebugレベルのログを出力したいのでLoggingの設定を追加します。

LoggingのデフォルトのログレベルはInfoのため、Loggingの設定を追加してログレベルを変更します。

LoggingのSettingsを参考にしてAppTestスクリプトをAppシーンに作成します。

  • AppTestスクリプトをAppディレクトリに作成します。
  • AppTestスクリプトをアタッチしたGameObjectをAppシーンに作成します。
using Extreal.Core.Logging;
using UnityEngine;

namespace ExtrealCoreLearning.App
{
public class AppTest : MonoBehaviour
{
private static void InitializeApp()
{
const LogLevel logLevel = LogLevel.Debug;
LoggingManager.Initialize(logLevel: logLevel);
}

private void Awake()
{
InitializeApp();
}
}
}
step

このままではLoggingの動作確認ができないので、Loggingの初期化直後にログ出力するように処理を追加します。

private static void InitializeApp()
{
const LogLevel logLevel = LogLevel.Debug;
LoggingManager.Initialize(logLevel: logLevel);

var logger = LoggingManager.GetLogger(nameof(AppTest));
if (logger.IsDebug())
{
logger.LogDebug("Hello, world!");
}
}

Appシーンを実行してみましょう。 ConsoleにHello, world!と出力されれば成功です。

Loggingアセンブリ設定

Add Stage Navigation

Stage Navigationをアプリケーションに追加します。

step

Stage NavigationはUniTaskUniRxに依存しているため先にUniTaskとUniRxを追加します。

UniTaskとUniRxはOpenUPMで提供されているためOpenUPMの設定を追加します。 Edit -> Project Settings... -> Package ManagerからScoped RegistriesにOpenUPMを追加します。

Unitask追加

  • Name:
    OpenUPM
  • URL:
    https://package.openupm.com
  • Scope(s):
    • UniTask
      com.cysharp
    • UniRx
      com.neuecc
step

UniTaskとUniRxを追加できたのでStage Navigationをスクリプトから使えるようにします。

Stage NavigationはCommonに依存してるのでCommonも追加します。

Loggingの時と同じ手順でPackage ManagerとAssembly Definitionを操作します。 CommonとStage NavigationのGit URLはCommonのPackageStage NavigationのPackageから取得します。 Assembly DefinitionにUniTaskとUniRxも設定します。

Stage Navigation追加

ステージ遷移を試すためタイトル画面を追加し、アプリ起動後すぐにタイトル画面に遷移させてみましょう。

step

まずはタイトル画面を追加します。

タイトル画面

  • Appディレクトリと同じ階層にTitleScreenディレクトリを作成します。
  • その中にTitleScreenシーンを作成します。
  • TitleScreenシーンのカメラなど初期設定されているGameObjectを削除しシーンを一旦空にします。
  • シーンにCanvasを追加します。インスペクタで次の設定をします。 タイトル画面Canvas
    • Canvas Scaler
      • UI Scale Mode: Scale With Screen Size
  • Canvasの配下にImageを追加します。Imageのインスペクタで次の設定をします。 タイトル画面Image
    • Rect Transform
      • Anchor Presets
        • X: stretch
        • Y: stretch
      • LeftからButtomまで全て0(Zero)
    • Image
      • Color
        • Hexadecimal: FF6F61
  • Imageの配下にText - TextMeshProを追加します。
    • Extreal Core Learningというタイトルを作ります。
    • サイズやポジションは自由に決めてください。
    • TMP Importerが表示されたらImport TMP Essentialsを選択します。TextMeshProの初回利用時のみTMP Importerが表示されます。インポートされたらTMP Importerを閉じます。
  • Imageの配下にButton - TextMeshProを追加します。
    • Goというボタンを作ります。
    • こちらもサイズやポジションは自由に決めてください。
step

タイトル画面を追加できたのでステージ設定を作成します。

Stage NavigationのSettingsを参照してステージ設定を作成します。 ステージ設定で作成する3つのタイプはAppディレクトリに配置します。

public enum StageName
{
TitleStage = 0,
}
public enum SceneName
{
TitleScreen = 100,
}

3つのタイプが作成できたらStageConfigオブジェクトを作ります。

Stage設定

  • AssetsメニューからStageConfigオブジェクトをAppディレクトリに作成します。
  • StageConfigオブジェクトにタイトル画面のステージを設定します。
step

これでステージ遷移の準備が整ったのでアプリ起動後すぐにタイトル画面に遷移させる処理を追加します。

  • AppTestスクリプトにSerializeFieldとStartメソッドを追加します。
  • インスペクタでStageConfigオブジェクトを指定します。
using Extreal.Core.Logging;
using Extreal.Core.StageNavigation;
using UnityEngine;

namespace ExtrealCoreLearning.App
{
public class AppTest : MonoBehaviour
{
private static void InitializeApp()
{
// Omitted due to no changes
}

private void Awake()
{
// Omitted due to no changes
}

[SerializeField] private StageConfig stageConfig;

private void Start()
{
var stageNavigator = new StageNavigator<StageName, SceneName>(stageConfig);
stageNavigator.ReplaceAsync(StageName.TitleStage);
}
}
}
step

Appシーンを実行してみましょう。

この状態でAppシーンを実行すると次のエラーになります。

Stage遷移エラー

Build SettingsのScenes In BuildにTitleScreenシーンを追加します。

step

もう一度Appシーンを実行してみましょう。

タイトル画面が表示され、Consoleに[Debug:StageNavigator] Transitions to 'TitleStage'と出ていれば成功です。

Stage遷移成功

Apply MV(R)P pattern

Extrealが提供するCoreの機能は以上となりますが、VContainerを追加してExtrealが想定するアプリケーションアーキテクチャに近づけていきたいと思います。

step

タイトル画面の遷移先となるアバター選択画面を追加します。

アバター選択画面は遷移先として使用するだけなのでタイトル画面をコピーして作ります。

アバター選択画面

  • Appディレクトリと同じ階層にAvatarSelectionScreenディレクトリを作成します。
  • その中にAvatarSelectionScreenシーンを作成します。
  • AvatarSelectionScreenシーンのカメラなど初期設定されているGameObjectを削除しシーンを一旦空にします。
  • TitleScreenシーンのCanvasとEventSystemをコピーしてAvatarSelectionScreenシーンにペーストします。
  • タイトルをAvatar Selectionに変更します。
step

続けてステージ設定を追加します。

アバター選択のステージ設定

  • StageNameにAvatarSelectionStage、SceneNameにAvatarSelectionScreenを追加します。
  • StageConfigオブジェクトのインスペクタでAvatarSelectionStageを追加します。

Build SettingsのScenes In BuildにAvatarSelectionScreenシーンを追加します。

UIとステージ設定の準備が整いました。

step

VContainerをアプリケーションに追加します。

VContainerはUniTaskやUniRxと同様にOpenUPMから取得します。

Edit -> Project Settings... -> Package ManagerからOpenUPMのScope(s)を追加します。

VContainer追加

  • VContainer:
    jp.hadashikick
step

続けてPackage ManagerとAssembly DefinitionにVContainerを追加します。

VContainer設定

  • Package ManagerでPackages: My Registriesを選択しVContainerをインストールします。
  • アプリケーションのAssembly DefinitionにVContainerを設定します。

これでVContainerが使える状態になりました。

VContainer

VContainerを使って確認用に作成したAppTestを作り変えます。

step

まずエントリーポイントとなるPresenterスクリプトを作成します。

Appディレクトリに作成します。 Appシーンが開始するとタイトル画面に遷移させます。

using System.Threading;
using Cysharp.Threading.Tasks;
using Extreal.Core.StageNavigation;
using VContainer.Unity;

namespace ExtrealCoreLearning.App
{
public class AppPresenter : IAsyncStartable
{
private StageNavigator<StageName, SceneName> stageNavigator;

public AppPresenter(StageNavigator<StageName, SceneName> stageNavigator)
{
this.stageNavigator = stageNavigator;
}

public async UniTask StartAsync(CancellationToken cancellation)
{
await stageNavigator.ReplaceAsync(StageName.TitleStage);
}
}
}
step

次にVContainerのLifetimeScopeとしてScopeスクリプトを作成します。

Appディレクトリに作成します。 ScopeスクリプトのAwakeをオーバーライドしてVContainerの処理よりも先にLoggingの設定を行います。

using Extreal.Core.Logging;
using Extreal.Core.StageNavigation;
using UnityEngine;
using VContainer;
using VContainer.Unity;

namespace ExtrealCoreLearning.App
{
public class AppScope : LifetimeScope
{
[SerializeField] private StageConfig stageConfig;

private static void InitializeApp()
{
const LogLevel logLevel = LogLevel.Debug;
LoggingManager.Initialize(logLevel: logLevel);

var logger = LoggingManager.GetLogger(nameof(AppScope));
if (logger.IsDebug())
{
logger.LogDebug("Hello, world!");
}
}

protected override void Awake()
{
InitializeApp();
base.Awake();
}

protected override void Configure(IContainerBuilder builder)
{
builder.RegisterComponent(stageConfig).AsImplementedInterfaces();
builder.Register<StageNavigator<StageName, SceneName>>(Lifetime.Singleton);

builder.RegisterEntryPoint<AppPresenter>();
}
}
}

StageConfigとStageNavigator、エントリーポイントとしてAppPresenterを登録しています。 この登録により、StageConfigがStageNavigatorに設定され、StageNavigatorがAppPresenterに設定されます。 このようにVContainerが提供するDIコンテナを使ってオブジェクトの構造を作り上げ、各オブジェクトが処理を実行できるようにします。

step

ScopeスクリプトをAppシーンに設定します。

AppScope

  • AppシーンにScopeという名前でAppScopeスクリプトをアタッチしたGameObjectを作成します。
  • StageConfigオブジェクトをインスペクタで設定します。
  • 不要になったのでAppTestスクリプトとAppTestスクリプトをアタッチしたAppシーンのGameObjectを削除します。

Appシーンを実行します。 先ほどと同様にタイトル画面とConsoleのログ出力が出ていれば成功です。

UniRx

UniRxを使ってタイトル画面のGoボタンを実装します。

step

タイトル画面に対応するViewスクリプトを作成します。

TitleScreenディレクトリに作成します。

using System;
using UniRx;
using UnityEngine;
using UnityEngine.UI;

namespace ExtrealCoreLearning.TitleScreen
{
public class TitleScreenView : MonoBehaviour
{
[SerializeField] private Button goButton;

public IObservable<Unit> OnGoButtonClicked
=> goButton.OnClickAsObservable().TakeUntilDestroy(this);
}
}

UniRxを使ってGoボタンが押された場合にイベントを通知するOnGoButtonClickedを定義しています。

step

次にGoボタンが押された場合にアバター選択画面に遷移させるPresenterスクリプトを作成します。

TitleScreenディレクトリに作成します。

using Cysharp.Threading.Tasks;
using Extreal.Core.Common.System;
using Extreal.Core.StageNavigation;
using ExtrealCoreLearning.App;
using UniRx;
using VContainer.Unity;

namespace ExtrealCoreLearning.TitleScreen
{
public class TitleScreenPresenter : DisposableBase, IInitializable
{
private StageNavigator<StageName, SceneName> stageNavigator;

private TitleScreenView titleScreenView;

private CompositeDisposable compositeDisposable = new CompositeDisposable();

public TitleScreenPresenter(StageNavigator<StageName, SceneName> stageNavigator,
TitleScreenView titleScreenView)
{
this.stageNavigator = stageNavigator;
this.titleScreenView = titleScreenView;
}

public void Initialize()
{
titleScreenView.OnGoButtonClicked.Subscribe(_ =>
{
stageNavigator.ReplaceAsync(StageName.AvatarSelectionStage).Forget();
}).AddTo(compositeDisposable);
}

protected override void ReleaseManagedResources()
{
compositeDisposable?.Dispose();
}
}
}

Goボタンのイベント通知とアバター選択画面への遷移をマッピングしています。

Disposeを行うクラスにはDispose Patternの実装が推奨されています。 Dispose Patternを適用するため、TitleScreenPresenterはCommonが提供するDisposableBaseクラスを継承し、ReleaseManagedResourcesメソッドにマネージドリソースの解放処理を実装しています。 Dispose Patternの適用方法はCommonを参照してください。

step

最後にViewやPresenterを紐づけるScopeスクリプトを作成します。

TitleScreenディレクトリに作成します。

using UnityEngine;
using VContainer;
using VContainer.Unity;

namespace ExtrealCoreLearning.TitleScreen
{
public class TitleScreenScope : LifetimeScope
{
[SerializeField] private TitleScreenView titleScreenView;

protected override void Configure(IContainerBuilder builder)
{
builder.RegisterComponent(titleScreenView);

builder.RegisterEntryPoint<TitleScreenPresenter>();
}
}
}

ViewスクリプトとScopeスクリプトをTitleScreenシーンに設定します。

AppScope

  • TitleScreenシーンにViewという名前でTitleScreenViewスクリプトをアタッチしたGameObjectを作成します。
  • Buttonオブジェクトをインスペクタで設定します。
  • TitleScreenシーンにScopeという名前でTitleScreenScopeスクリプトをアタッチしたGameObjectを作成します。
  • Viewオブジェクトをインスペクタで設定します。
step

Appシーンを実行してみましょう。

この状態でAppシーンを実行すると次のエラーになります。

AppScope

TitleScreenPresenterにStageNavigatorを設定していますがスコープに登録されていないためエラーとなっています。 StageNavigatorはAppScope、TitleScreenPresenterはTitleScreenScopeと異なるスコープのためこのエラーが発生しています。

VContainerではスコープの親を指定してオブジェクトの検索範囲を親まで広げることができます。 TitleScreenScopeのインスペクタでParentにAppScopeを指定するとこのエラーが解消します。

AppScope

このようにスコープは階層を意識して作成します。 共通利用されるスコープをより親となるように全体のスコープを設計します。

step

もう一度Appシーンを実行してみましょう。

タイトル画面からアバター選択画面に遷移できれば成功です。

Next Step

これでCoreのハンズオンは終了です。 お疲れさまでした。

このハンズオンを通じてExtrealが想定するアプリケーションアーキテクチャに必要な機能を構築済みです。 次のステップとしてハンズオンで構築したアーキテクチャがより本格的なアプリケーションでどのように使われるのか関心があると思います。 その期待に応えるため、より本格的な実装例としてSample Applicationを提供しています。 ぜひSample Applicationをご覧ください。