スケーラブルなSelf-hosted Runnerを立ち上げるために"terraform-aws-github-runner"を使ってみた

こんばんは、サーバサイドエンジニアの黒岩(@kro96_xr)です。
今回は先週の記事で触れられていたterraform-aws-github-runnerの環境構築を行ったときの作業内容を書いていきたいと思います。

先週の記事はこちら
synamon.hatenablog.com

構成図

先週の記事にもありましたがインフラ構成図は以下のようになっています。(引用元リンク)

処理フローを文章化すると以下のようになるでしょうか。

  • Github Appsでリポジトリに対するイベント発生を検知
  • Webhookが発火し、"Webhook" Lambdaが実行され、メッセージをSQSにキューイング
  • "Scale Up" Lambdaがメッセージを受け取り、スポットインスタンスを生成
  • インスタンス起動時にuser_dataスクリプトでエージェント等をインストール ⇒ ランナーとして登録
  • 処理の実行
  • "Scale Down" Lambdaが定期的にインスタンスをチェックし、不要となったインスタンスとランナーを削除

上記の通り、環境構築を行うためにはAWS側の設定Github側の設定が必要となります。
前者の設定はTerraformから実行できますが、後者の設定は手動で行う必要があります。

基本的にはREADMEを読めばわかるかと思いますが、設定方法を見ていきましょう。

STEP1: GithubAppの作成

まず、GithubAppを作成していきます。
名前等は自由に決め、Webhookのチェックボックスをこの時点では外しておきます

パーミッションはActions、Checks、MetadataをRead-onlyにします。
また、ランナーをリポジトリレベルで使用するため、AdministrationをRead & writeとします。

組織レベルで使用する場合はOrganization permissionsのSelf-hosted runnersをRead & writeとするそうです。
こちらは今回は試していないので画像等は割愛します。

アプリを保存するとAppIDとClientIDが発行されるので保存しておきます。

作成されたAppの秘密鍵を生成し、保存しておきます。

STEP2: Terraformの設定

続いてTerraformの設定になります。

まず、Lambdaのスクリプトをダウンロードします。
Github上のリリースページからダウンロード可能ですので最新版を落としておきます。
これらのファイルのパスをmain.tf内で指定するので控えておいてください。

次に、main.tfを書いていきます。
READMEにサンプルがありますが、今回はランナーとしてWindowsを使用するために修正を行っています。
先に全体を載せておきます。なお、VPCの作成は事前に行っている想定です。

# ランダムIDの生成
resource "random_id" "random" {
  byte_length = 20
}

module "github-runner" {
  source  = "philips-labs/github-runner/aws"
  version = "v0.36.0"

  # 事前に作成したVPCを指定
  aws_region = "ap-northeast-1"
  vpc_id     = <VPC ID>
  subnet_ids = [<サブネットID>]

  # 環境名(プレフィックス)
  environment = <任意の文字列>

  # GithubAppの設定
  github_app = {
    key_base64     =    <秘密鍵をBase64エンコードした文字列>
    id                     =    <GithubApp ID>
    webhook_secret =    random_id.random.hex
  }

  # 先にダウンロードしておいたLambdaのパスを指定
  webhook_lambda_zip                = "lambdas-download/webhook.zip"
  runner_binaries_syncer_lambda_zip = "lambdas-download/runner-binaries-syncer.zip"
  runners_lambda_zip                = "lambdas-download/runners.zip"
  enable_organization_runners       = false
  
  # OS指定
  runner_os = "windows"

  # ランナーのラベル指定
  runner_extra_labels = "default,example"

  # Runnerの立ち上げ待ち時間
  runner_boot_time_in_minutes = 20

  # webhookの実行を遅延させる(秒)
  delay_webhook_event = 5

  # スケールダウンチェックの頻度をcronで設定
  scale_down_schedule_expression = "cron(0 * * * ? *)"
}

ポイントはGithubApp作成時に生成した情報をセットすることです。

  # GithubAppの設定
  github_app = {
    key_base64     =    <秘密鍵をBase64エンコードした文字列>
    id                     =    <GithubApp ID>
    webhook_secret =    random_id.random.hex
  }

もちろんtfvarsで渡しても大丈夫です。というかtfvarsに書いてgitignoreした方がセキュリティ上いいと思います。

key_base64は秘密鍵をさらにBase64エンコードした文字列となりますので気を付けてください。
下記のissueを見るまで私は見落としていました…
PEM routines:get_name:no start line · Issue #40 · philips-labs/terraform-aws-github-runner · GitHub

書けたらterraform init, terraform plan, terraform applyを実行します。

$ terraform init
$ terraform plan
$ terraform apply

その他追加の設定

基本的な設定は上記で大丈夫だと思いますが、GithubにInputパラメータがありますので用途に合わせて調整してみてください。
弊社では下記のような設定を追加しています。

  • ラベル設定の変更

複数のUnityバージョンに対応できるようにラベルを変更しています。

  # ラベル設定
  runner_extra_labels = "<Unityバージョン>"
  runner_enable_workflow_job_labels_check = true
  • AMIの設定

カスタムAMIを使用しているためその設定を追加しています。

  # ami
  ami_filter = {
    name = [<自作したAMI名>]
  }
  ami_owners = ["<AMIを所有するアカウント名>"]
  • EBSの設定

カスタムAMIがストレージをだいぶ食っていたのでサイズをあげました。

  # EBSのサイズ(GB)
  volume_size = <EBSサイズ>

STEP3: Webhookの設定

Webhookの情報をGithubAppに設定していきます。
下記を実行するとWebhook URLとWebhook Secretが取得できるので控えておきます。

$ terraform output -raw webhook_secret

GithubAppの設定画面に戻り、Webhookを有効化してそれぞれの値を設定していきます。

次にWebhookを発火するイベントを設定します。
Permission & eventsからWorkflow jobにチェックを入れます。

最後にリポジトリにAppをインストールします。
Organization>Settings>Integrations>Github Appsから作成したAppを選択、編集します。
リポジトリを選択してインストールすることで、該当リポジトリでランナーを使えるようになります。

STEP4: GIthub Actionsの設定

runs-onの項目を下記のように設定すればOKです。

runs-on: [self-hosted, windows, x64, <設定したラベル名>]  

おわりに

以上、"terraform-aws-github-runner"を使ったSelf-hosted Runnerの構築でした。
今回紹介した機能はまだ一部ですのでぜひみなさん触ってみてはいかがでしょうか?

また、まだ詳しく読めていないのですが下記のような方法もあるようでした。
私自身まだまだ詳しくない領域なので知見を共有していただけると嬉しいです!
HashiCorp NomadでGitHub Actions Self-hosted Runnerを簡単にautoscale運用する

参考リンク

github.com

qiita.com

zenn.dev

AWSを使ったクラウドUnityビルド環境の構築~ビルドサーバー構築編~

エンジニアの岡村です。この記事は以前掲載した『AWSを使ったクラウドUnityビルド環境の構築~ライセンスサーバー構築編~』の続編であり、シリーズの2記事目です。

synamon.hatenablog.com

前回の記事ではEC2を使ってライセンスサーバーを作成したので、そこに接続してライセンス認証をしてビルドを行うWindowsマシン(self-hosted runner)をAWS上に作っていきます。

UnityのCI事情

Unityを使ってCIを行おうとしたときにまず当たるのが、世のCIサービスのUnity対応の渋さです。サービス側がホストしてくれているマシンにUnityがインストールされている事はまずなく、エディタの処理もサイズも軽いわけではないのでデフォルトで用意されている非力なマシンに毎回ダウンロード、インストールして動かすとかなりの無駄が発生してしまいます。公式が提供しているUnity Cloud Buildが一番簡単に使えて楽なのですが、ビルドに時間が掛かってしまうのと、カスタマイズの低さに難があります。

かといって自前のマシンにインストールしたUnityを動かすのにも色々な落とし穴があったりします。セットアップされたUnityインストール済みのマシンを適当なCIツールから叩くのであればまだ楽ですが、例えばコンテナベースでやろうとしたりすると、大量の地雷を踏むこととなります(Windows Server Coreでサイレントクラッシュするなど)。最近のUnityはコマンドライン経由でのインストールや実行、前の記事で紹介したフローティングライセンスをサポートしてくれており、昔よりはだいぶ扱いやすくなったのですが、それでも叩き方にクセがあったりして一筋縄ではいきません。これはクラウドに限らず、物理PCでCIを回すときにも大変苦戦することになりました。今回はその知見もあったのでクラウド移行は割と楽に出来るかと思っていたのですが、そもそもWindows×UnityがCIしんどい環境の掛け合わせなのもあり、AWSに乗せる中でまたしても色々苦労することになりました。

EC2 スポットインスタンスの活用

前述のとおりUnity Editorは軽いアプリケーションではありません。EC2インスタンスは自由なスペックで簡単に用意できてメンテナンスフリーなのが魅力的ですが、強いインスタンスを立てると相応にお金がかかってしまいます。なので不要な時には止めておきたいのですが、その管理も出来れば自動で行いたいです。自動化するとAWSのスポットインスタンスを利用してさらに値段を抑える事が出来ます。という事で今回は必要になったタイミングでEC2のスポットインスタンスを起動し、不要になったら(しばらく利用されなかったら)シャットダウンする仕組みの構築を目指してみました。

philips-labs/terraform-aws-github-runnerの採用

github.com

今回スポットインスタンスの管理にはこちらのOSSを利用させて頂きました。他にもいくつか似た機能を実現するOSSはあったのですが、今回こちらを選んだのは主に以下のような点でした。

  • AWS, EC2を利用する前提要件を満たしている(コンテナベースもありましたが、コンテナでのビルドは以前挑戦して失敗したので……)
  • Terraformで記述されている(社内でTerraformを利用している為)
  • Windows, Linuxのインスタンスに対応している(本当はMacも欲しかったのですが、UnityのビルドはWindows上で行えるのと、Github-hosted runnerでXCode入りのMacが使えるので妥協しました)
  • コミュニティが活発である

ちなみに、この辺りのOSS選定基準は以前佐藤さんが書かれたこちらの記事も参考になります。

synamon.hatenablog.com

OSSを利用してrunner用の環境を構築する

terraform-aws-github-runnerを使うと、以下のような構造が出来上がります。 (画像はGitHubより引用)

LambdaのコードなどはOSS側で用意してもらえますが、GitHubと繋ぐための設定や、実際に動かすマシンイメージ(後述)と設定は自分で用意する必要があります。基本的な手順はOSSのドキュメントに記載されている為割愛します。

設定項目 設定した値(参考)
runner_extra_labels "self-hosted,unity-2021.2.11f1"
instance_types "t3.xlarge"
volume_size 50

※ここのラベルは今後GitHub Actionsを作成するときにrunnerを指定する為に利用します。

Terraformの知識があれば構築にそう苦労することはないかと思います。(自分は知識がなかったので社内の得意なメンバーにお願いしました。この場を借りて感謝します。)

ただし、ドキュメント中に「GitHubのAppKeyをBase64エンコードしろ」という指示がありますが、これはBase64でエンコードされているpemキーをさらにBase64でエンコードしろという意味なので、ここだけ気をつけてください。

runner用のVPCとライセンスサーバーのVPCをVPC Peeringで接続する

プライベートIPでライセンスサーバーとのやり取りを行う場合には、VPC Peeringを利用してVPC同士を接続すると便利です。

※IPアドレスは一例です

ピアリング接続を作成した後、それぞれのルートテーブルにルートを追加し、それぞれのセキュリティグループで送信元SGを許可するように設定してください。ライセンスサーバーが動いていれば、ランナー用に用意したサブネット内に適当なインスタンスを立ててhttp://***.***.***.***:8080/v1/status(プライベートIPアドレス)にアクセスすれば疎通確認ができます。

Runner用のAMIを構築する

Unityをインストールしたマシン(を作る為のマシンイメージ)を用意していきます。今回はAWSのサービスの一つであるEC2 Image Builderを利用して作成しました。

aws.amazon.com

手動でEC2インスタンスを立て、Unityをインストールした後イメージ化してもいいのですが、もし手動で構築する場合は、Unityインストール後にSysprepを実行する必要があります。

EC2 Image Builderで利用するための、Unityをインストールするコンポーネントを作成します。サンプルのyamlファイルを紹介するので、http://***.***.***.***:8080の部分を前回の記事でセットアップしたライセンスサーバーのアドレスに置き換えてください。また、 ここでインストールするエディタのバージョンも指定しているので、利用するバージョンにあわせて書き換えてください。

name: UnityInstallDocument
description: 
schemaVersion: 1.0

phases:
  - name: build
    steps:
      - name: PowerShellVersion
        action: ExecutePowerShell
        inputs:
          commands:
            - $PSVersionTable
      - name: DownloadHub
        action: ExecutePowerShell
        inputs:
          commands:
            - Invoke-WebRequest https://public-cdn.cloud.unity3d.com/hub/prod/UnityHubSetup.exe -OutFile UnityHubSetup.exe
      - name: InstallHub
        action: ExecuteBinary
        inputs:
          path: .\UnityHubSetup.exe
          arguments:
            - /S
      - name: InstallEditor
        action: ExecutePowerShell
        inputs:
          commands:
            - 'Start-Process -FilePath "C:\Program Files\Unity Hub\Unity Hub.exe" -Wait -ArgumentList "-- --headless install --version 2021.3.0f1 --changeset 6eacc8284459 -m windows-il2cpp -m android -m android-sdk-ndk-tools -m android-open-jdk -m linux-il2cpp -m ios --childModules"; exit 0'
      - name: DownloadVisualStudio
        action: ExecutePowerShell
        inputs:
          commands:
            - Invoke-WebRequest https://aka.ms/vs/17/release/vs_buildtools.exe -OutFile vs_buildtools.exe
      - name: InstallVisualStudio
        action: ExecuteBinary
        inputs:
          path: .\vs_buildtools.exe
          arguments:
            - --quiet
            - --wait
            - --add
            - Microsoft.VisualStudio.Component.VC.Tools.x86.x64
            - --add
            - Microsoft.VisualStudio.Component.Windows10SDK.20348
      - name: RegisterEnvironmentVariableForVisualStudio
        action: ExecutePowerShell
        inputs:
          commands:
            - '[Environment]::SetEnvironmentVariable("VS170COMNTOOLS", "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools", "Machine")'
      - name: CreateLicenseFile
        action: ExecutePowerShell
        inputs:
          commands:
            - 'New-Item "$env:PROGRAMDATA\Unity\config" -ItemType Directory -Force'
            - '@{licensingServiceBaseUrl = "http://***.***.***.***:8080"; enableEntitlementLicensing = $true; clientConnectTimeoutSec = 10; clientHandshakeTimeoutSec = 10} | ConvertTo-Json | Out-File "$env:PROGRAMDATA\Unity\config\services-config.json" -Encoding ascii;'

※上記コード中でUnity HubをExecuteBinaryではなくExecutePowerShellで実行しているのは、HubがEditorインストールを正常完了してもリターンコードに1を返す謎仕様でパイプラインが失敗扱いになるのを回避する為です。

※2022年4月15日追記 上記コード中にPowerShellでjsonファイルを作成している箇所がありますが、-Encoding utf8を指定していたのを-Encoding asciiに修正しました。PowerShellのOut-Fileの引数であるutf8はBOM付きutf-8を指しています。BOM付きで保存されたservices-config.jsonを使用した場合、Unityを動かしたときにライセンス認証に失敗してしまうため、BOM無しで保存するように修正する必要がありました。BOM無しutf-8で保存する方法は幾つかあるのですが、今回はjson内でascii範囲外の文字を使っていないのでasciiで保存する事で代用しています。(無いとは思いますが)日本語ドメイン等を使っていたらutf-8で保存しなければならないので頑張ってください。PowerShell 6以上であればutf8NoBOMが使えるのですが、自分が試したときのバージョンは5.1.20348.558でした。

※2022年5月2日追記 Unityのモジュールのインストールが上手く行っていなかったため修正、またWindowsのIL2CPPビルドを行う際にVisualStuidoのSDKが必要だったため、そのインストールプロセスを追加しました。Unityのインストール時にvisualstudioをインストールするオプションもありますが、そちらはパイプライン上ではうまく動作しませんでした。

コンポーネントができたら、それを基にイメージレシピを作成します。

設定項目 設定した値(参考)
ベースイメージ Windows Server 2022 English Full ECS Optimised x86の最新バージョン
作業ディレクトリ C:/
コンポーネント 直前に作成したビルドコンポーネントをアタッチ
ストレージ gp2 50GiB(終了時に削除)

インフラ設定のVPC等は一旦ネットに繋がれば十分ですが、きちんとテストしたい場合は実際に動かす環境を利用するといいかもしれません。ビルド時に利用するIAMロールには、AmazonSSMManagedInstanceCoreEC2InstanceProfileForImageBuilderポリシーのアタッチが最低限必要です。(必要に応じて追加してください。)

イメージレシピ、インフラ設定ができたらそれを基にパイプラインを作成しましょう。スケジュールは今の所AMI作成まで自動化する予定はないので今回は手動に設定しました。ディストリビューションはデフォルトで問題ありません。

パイプラインが完成したら、アクション>パイプラインを実行する でamiをビルドする事が出来るようになります。もしコンポーネントの設定ミスでビルドに失敗しても、パイプラインの詳細からCloudWatchに記録されたログを確認することが可能です。

AMIが出来上がったら、前述したterraformの設定ファイルにAMI IDを設定して構築を行いましょう。 OSSの説明書通りにAWS側のインフラ構築、及びGitHub Appを作成してください。

次回予告

これでUnityを実行するための環境が整いました。次回(またしても日付未定)は、今回作ったランナーと前回作ったライセンスサーバーを接続して、GitHub Actionsのコードを書いてビルドを行っていきます。

Unityの(New)InputSystemでBindingを好きにカスタマイズできるCompositeBindingsの紹介

はじめに

こんにちは、エンジニアリングマネージャーの渡辺(@mochi_neko_7)です。

少し前まではマネジメントや採用を中心にやっていましたが、最近はUnityの開発をメインにやるようになりました。

今開発しているプロジェクトでは、Unityの新しいInputSystemである(New)InputSystemを導入しています。

こちらは複数デバイスでの入力の取り回しにとても便利なシステムで、特にActionという抽象化がとても扱いやすいです。

ただLowLevelなところは以前からあるInputSystemとそこまで変わらないですし、欲しい入力の形が思い通りに用意されているとは限らなかったりしますので、ちょっと凝った入力(例えばTouchscreenでのスマホ特有の操作など)を扱おうとすると難しい場面があります。

今回はそんな時に便利な、InputSystemでの入力の取得方法をある程度カスタマイズできる仕組みであるCompositeBindingsについて紹介します。

(New)InputSystemの設計のおさらい

(New)InputSystemの公式のドキュメントはこちらです。 2022/2/18現在の最新版はver1.3です。

docs.unity3d.com

インストール方法などは公式ドキュメントを参照してください。

また、本記事以外にも解説している記事や動画がありますので、まだあまり慣れていない方はこれらを見ていただくと良いかと思います。

forpro.unity3d.jp

www.youtube.com

ここでは(New)InputSystemの全体的な設計だけおさらいしましょう。

細かい話は公式のドキュメント

docs.unity3d.com

にもありますが、今回は InputActionAsset を触る視点での構造に注目します。

形としてはシンプルなヒエラルキー構造で、上から辿ると、

  • Action Asset
    • Action Maps
      • Actions
        • Bindings
          • (DeviceInputs)

のような順に並びます。

ここで出てくる用語は公式のまとめ

docs.unity3d.com

が詳しいですが、ポイントは

  • Actions:アプリケーション内で実現したい動作
  • Bindings:Actionとデバイスの入力を紐づけるもの
  • DeviceInputs:デバイスの入力

のようにActionという概念を抽象化して、Bindingのレイヤーを挟むことによって入力デバイスの変更に強い設計になっていることです。

今回はあまり説明はしませんが、デバイスの変更の仕組みはControlSchemesを参照してください。

docs.unity3d.com

CompositeBindingsとは

Bindingsの中にCompositeBindingsという項目があります。

docs.unity3d.com

Composite=合成のようなニュアンスの言葉です。

具体的なサンプルとして分かりやすいものは、PCゲームでよくあるWASDキーでの移動方法で、W、A、S、Dの異なる4つの入力を、1つのVector2にまとめることができます。

  • Vector2
    • W(Up方向)
    • A(Left方向)
    • S(Down方向)
    • D(Right方向)

このように複数の入力を加工してActionに渡せるのがCompositeBindingsというわけです。

デフォルトでいくつかのパターンのCompositeBindingsがあります。

  • Positive\Negative Bindings (1D axis)
  • Up\Down\Left\Right Bindings (2D axis)
  • Up\Down\Left\Right\Forward\Backward Bindings (3D axis)
  • Binding With One Modifier
  • Binging With Two Modifiers

お察しの方もいるとは思いますが、このCompositeの部分を自分でカスタマイズできれば、ControlSchemeの仕組みに乗りながら複数デバイスの様々な入力に対応できるかなり柔軟な入力システムを作れることになります。

そのカスタマイズをするための仕組みが、今回のメインテーマのInputBindingCompositeになります。

CompositeBindingsのカスタマイズの仕方

どうやってカスタマイズするのか?というのは公式のドキュメント

docs.unity3d.com

にもガイドがあるのでこれのCustomCompositeのコメントを上から読めば分かるのですが、英語ですし、今回はざっくりかいつまんで解説しましょう。

まず基本的な形式は以下のような形になります。

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Utilities;

#if UNITY_EDITOR
using UnityEditor;
#endif

#if UNITY_EDITOR
[InitializeOnLoad]
#endif

[DisplayStringFormat(nameof(CustomInputBindingComposite))]
internal sealed class CustomInputBindingComposite : InputBindingComposite<T>
{

    #if UNITY_EDITOR
    static CustomInputBindingComposite()
    {
        Initialize();
    }
    #endif

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void Initialize()
    {
        InputSystem.RegisterBindingComposite<CustomInputBindingComposite>();
    }

    [InputControl(layout = "Button")] public int anInputControl;

    public override T ReadValue(ref InputBindingCompositeContext context)
    {
        // Compute T value
    }

    public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
    {
        // Compute magnitude of T value
    }

}

1. InputBindingCompositeの継承とセットアップ

InputBindingComposite<T>を継承したクラスを用意することから始まります。

internal sealed class CustomInputBindingComposite : InputBindingComposite<T>
{
    ...
}

CustomInputBindingCompositeはもちろん任意の名前で構いません。

<T>には入力として扱いたい型を指定してください。

特に制約もなく、自前で定義した型も使用できます。

もちろんAccesibilityは任意で構いません。

ClassのAttributeと、Class内のConstructor、InitializeメソッドはInputSystemに乗せるためのお作法です。

#if UNITY_EDITOR
[InitializeOnLoad]
#endif
...
    #if UNITY_EDITOR
    static CustomInputBindingComposite()
    {
        Initialize();
    }
    #endif

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void Initialize()
    {
        InputSystem.RegisterBindingComposite<CustomInputBindingComposite>();
    }

2. InputControlの追加

次にデバイスから受け取りたい入力のInputControlを追加します。

 [InputControl(layout = "Button")] public int anInputControl;

Attributeのlayout = "Button"の部分を指定することで、設定できるデバイス入力をフィルターすることができます。

また、1つだけという制約はもちろんありませんので、好きなだけ追加して定義することができます。

ちなみに型がintなのは、ここで追加したInputControlたちはそれぞれPartとして区別されていて、そのインデックスに対応する変数PartNumberだからだと思います。

3. ReadValueメソッドの記述

ReadValue<T>のメソッドで、具体的にどのような入力を流すのかを記述します。

...
    public override T ReadValue(ref InputBindingCompositeContext context)
    {
        // Compute T value
    }

先ほど定義したInputControlの値は、以下のようにInputBindingCompositeContext.ReadValue<>()で取得することができます。

...
    public override T ReadValue(ref InputBindingCompositeContext context)
    {
        var value = context.ReadValue<float>(anInputControl);
        ...
    }

4. EvaluateMagnitudeメソッドの記述

もう一つoverrideすべきメソッドが、EvaluateMagnitudeになります。

...
    public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
    {
        ...
    }

この結果の値は、Interactionの管理に使用されるようなので、特にButtonなどで結果を受けたい場合に重要になります。

(overrideしないと、まったくイベントが発行されない、なんてことになってしまいます)

...

基本的な説明は以上になります。

入力の取得部分は特に他と変わりません。

あとはInputActionAssetのBindingsで自作したCompositeBindingが追加できることと、実際に思った通りに入力が取れることを確認してみてください。

CompositeBindingsの応用的な使い方

少しだけ応用に踏み込んだ話もしましょう。

InputControlを追加する→ReadValue()で使用する、というのが基本的な入力の取得フローですが、実はInputSystemは古いInputManagerのように直接デバイスを指定して入力を取得する低レベルAPIも用意されています。

Touchの例:

var touches = Touchscreen.current.touches; // Touchの0~9の配列を取得

これをReadValue()の中で好きに呼び出すことができるので、かなり応用の幅が広がると思います。

Touchまわりでの具体的な使用例も少し挙げましょう。

  • TapしたPositionを取りたい
  • Screen上の特定領域のみでのTouch操作を取りたい
  • Screen上での仮想Joystick操作を取りたい
  • EnhancedTouchを利用したい
  • 複数のデータをまとめた自作型で入力を送りたい
  • etc...

おわりに

CompositeBindingsは使いようによってはかなり便利なものなのですが、まだドキュメントが少なかったり、全体の仕組みが分からないととっつきづらいのかなと思います。

ですが適切に扱うことで、デバイスに依存する複雑な入力処理を、Actionに紐づくロジックと(InputSystemを跨ぐ形で)きれいに分離することができます。

つまりActionに紐づくロジック側では、デバイスの差異を(ほとんど)気にすることなく実装をすることができるのです。

特に対応プラットフォームが多いプロジェクトでは有用だと思いますので、これを機に触ってみていただけると良いかなと思います。

また、自分自身も手探りで触りながら理解している部分も多いため、もし間違った記述に気づいた方がいましたらコメントいただけますと幸いです。

AWSを使ったクラウドUnityビルド環境の構築~ライセンスサーバー構築編~

エンジニアの岡村です。

最近まで、弊社でのUnityを使ったクライアント開発では、社内の物理サーバーに立てたgitとJenkinsを使ってバージョン管理及びCIを行っていました。GitHubは主にサーバー開発に使われており、Unityのコードは検証用のような小さなものを置いている位の状態でした。

しかし、UnityプロジェクトのGitHubでの管理が本格的に始まり、それに合わせてCI/CD環境もクラウド上に構築する機運が高まってきました。

この機会にCIの構成を見直し、より便利なUnityクライアントのCI環境の構築を目指して試行錯誤したので、何回かに分けてその内容をお届けしたいと思います。

Unityのフローティングライセンス

f:id:Sokuhatiku:20220328213403p:plain

Unityには通常のユーザーID, パスワード, シリアルナンバーで認証するライセンスのほかに、ライセンスサーバーを自分で建ててそこにライセンスを登録し、クライアントがUnity起動時に要求し、終了時に返却し、マシン間で限られたライセンスを融通できる、フローティングライセンス形態のライセンスが存在します。

unity.com

このライセンス形態は、Unityを実行するマシンに共通の設定ファイルを配置するだけで使えるので、Unityを実行するマシンを複数セットアップする場合に便利です。ただし、現状では、後述するUnity Build Server ライセンス以外はこの形態では提供されていないようです。

フローティングライセンスとして提供されているUnity Build Server ライセンスは、名前の通りUnityプロジェクトをビルドするサーバー向けのライセンスで、エディタをバッチモード以外で起動できないという制約がある代わりに、1シート当たりの値段を約1/3に抑えることができます。

Unity Pro Unity Pro Build Server
ライセンス形態 シリアルキー フローティング
認証に必要な情報 [ID, PASS, シリアルキー]
or
[手動ライセンスファイル]
認証サーバーのアドレス
価格 217,800円/年 79,200円/年
Editor GUI ×

CIを回す上ではGUIを開けないのは特にデメリットではなく、フローティングライセンスのセットアップの楽さ、値段の安さのメリットがあった為、このライセンスを使ってCI環境を作ってみることにしました。

ライセンスサーバーの構築

フローティングライセンスはクライアントの構築は楽ですが、自分でライセンスサーバーをホスティングする必要があります。実際に構築作業を行った内容をこれから紹介しますが、基本的にドキュメント通りにやれば構成できるので細かい手順は省略しつつ、要所要所を紹介しておきます。

ちなみに、構築にはAWSを選択しました。これは単純にサーバーを触るメンバーでAWSに慣れている人が多かった為です。

EC2インスタンスを立てる

f:id:Sokuhatiku:20220328215243p:plain

今回は上図のような最低限の構成でサーバーを構築します。SSHはきちんとIP制限を掛け、それ以外のトラフィックは許可しないようACLやセキュリティグループを設定しましょう。

ライセンスサーバーを実行するEC2のインスタンスタイプはt2.micro、HDD容量は10GBもあればまず不足することはないでしょう。OSはAmazon Linux2を選択しました。ライセンスサーバーはWindows/Linuxに対応しているので、どちらでも構いません。

ライセンスサーバーをセットアップする

ライセンスサーバーのセットアップの際には、サーバーをUnity IDポータルに登録し、生成したライセンスファイルをサーバーに読み込ませる必要があります。一度サーバーにライセンスシートを割り当てると手動では解除できず、リセットするにはUnity社に問い合わせる必要があるので注意してください。

まずはUnity公式からライセンスサーバーの実行ファイルとドキュメントを入手します。Unity Build Serverライセンスが利用可能であれば、Unity IDポータルから > 組織 > サブスクリプションとサービス > Unity Pro (or Enterprise) Build Server > ライセンスサーバーの設定 > Download new server と進むことで実行ファイル、及びドキュメントをダウンロードできます。 f:id:Sokuhatiku:20220329172612p:plain f:id:Sokuhatiku:20220328175813p:plain f:id:Sokuhatiku:20220328175836p:plain

このファイルはUnity IDポータルにログインしないとダウンロードできない為、一度作業PCにダウンロードしてからEC2インスタンスにアップロードするか、EC2インスタンス内でUnity IDポータルにログインする必要があります。

自分は今回のセットアップにはブラウザを使ったEC2 Instance Connectを利用しており、そのままではSCPによるファイル転送ができなかった為、一度手元にダウンロードしてからngrokを使ってEC2インスタンス内でwgetする方法を取りました。

zipをEC2インスタンス内にダウンロードしたら、ファイルを/opt/UnityLicensingServer/以下に解凍し、一緒にダウンロードしたドキュメントのQuickStartGuideの通りにセットアップを進めて下さい。

細かい手順はドキュメントに丁寧に書いてある為ここでの紹介は省きますが、自分が構築した際にハマったポイントや注意点を紹介します。

注意:セットアップ時に使用するユーザー権限について

/opt/UnityLicensingServer/フォルダをchown -Rでec2-user(ログイン中のユーザー)に所有権を移し、マニュアル内で指示されていない限りnot sudoで進めて下さい。./Unity.Licensing.Server setup はsudoでも動くのですが、そうした場合設定ファイルがrootユーザーの場所に生成され、かつサービスとして実行する際にセットアップで使っているユーザーのhomeを見に行くので、設定ファイルが読み込めず再起動を繰り返す状態になってしまいます。

注意:セットアップ時に使用するサーバーの情報について

ライセンスサーバーを構築する際、これらの情報がサーバー情報としてregistration-request.xmlに記録されます。

  • MACアドレス
  • プラットフォーム(Linuxの場合はUnix)
  • プロセッサの数
  • マシン名(hostname)

Unity ID Portalにサーバー情報をアップロードした後にEC2上のライセンスサーバーを再構築する際は、これらを変化させないように気を付けてください。

MACアドレスについては、EC2インスタンスが利用しているネットワークインターフェイスを再利用することで別のマシンに持ち越すことができます。デフォルトではEC2インスタンスを終了するとネットワークインターフェースも削除されてしまうので、忘れずに削除されない設定に変更しておきましょう。

f:id:Sokuhatiku:20220328193632p:plain

また、マシン名の方はこの辺りのページを参考にすると変更することが可能です。

注意:registration-request.xmlのダウンロードについて

前述のとおり自分はブラウザ上のEC2 Instance Connectを使ってセットアップを行っていたのですが、setupコマンドの実行後に吐き出されるregistration-request.xmlをダウンロードするのが面倒だった為、catで吐き出してコンソールをコピペしていました。するとその際シークレットキーに改行が入ってしまい、それが原因でキーの不一致が発生してライセンスの登録が出来ないというトラブルに陥りました。きちんとファイル転送が使える手段で接続している場合は全く考慮する必要はありませんが、一応ハマりポイントとして書き残しておきます。

次回予告

ライセンスサーバーの構築ができたら、同じサブネット上に別のインスタンスを立ててUnityをインストールし、ドキュメントで指示されている通りにservices-config.jsonを置けばUnityをバッチモードで実行出来るようになります。しかし、Unityを動かすマシンを常時起動していると料金がかかり、手動で落とすと手間がかかります。その為、次回は必要になったタイミングでEC2インスタンスを起動する仕組みをご紹介します。

synamon.hatenablog.com

Photon Realtimeのネットワーク上でNetcode for GameObjectsで作ったアプリケーションを利用してみる

はじめに

エンジニアの松原です。最近はメタバース関連の話題をあらゆる媒体で見るようになりましたが、開発者目線ではメタバースでのインフラ部分で利用されているリアルタイムネットワークのクライアントやサーバーの設計が気になるところです。
プレイヤーの参加人数によってサーバーやクライアントアプリケーションに求められる機能や構成が変わってくるので、「これをやれば正解」というものが無いのがリアルタイムネットワークの面白さでもあります。

さて、これまで本ブログでは以前の記事(その1その2その3その4)のように、Netcode for GameObjectsで利用するコンポーネントやセットアップ方法などを紹介をしてきました。 今回はNetcode for GameObjectsのもう一つの目玉機能であるTransportsについて紹介したいと思います。
この機能はサードパーティー製のネットワーククライアントをプラグインとして取り込むことができ、これまでの記事で書かれたコード実装を変更せずに、そのサードパーティー製ネットワークの仕組みをそのまま利用できるようなる環境を用意してくれます。

docs-multiplayer.unity3d.com

具体例として、Photon Realtimeのネットワークを利用して、Netcode for GameObjectsを動かせるようにしていきます。今回はMultiplayer Community Contributionsと呼ばれている有志達によって実装されているパッケージを利用します。

github.com

今回の内容を反映したものを個人のGitHubリポジトリにアップロードしておりますので、もしよろしければ触ってみてください!

github.com

今回の機能を反映したサンプルシーン名は SampleScene_PhotonNetwork.unityになります。



Community Transportsをインストールする

まずはメニューの Window > PackageManager のウィンドウを表示します。表示できたらウィンドウの左上の【+(プラスマーク)】のプルダウンをクリックし、【Add package from git URL】をクリックします。



赤枠の欄内に下記に指定するURLを入力します。



このURLはInstalling Community Transportsの指定の通り、PackageManagerからインストール際のURL指定を指示通り書き換えたものを使用します。

Use the following URL in the package manager to add a transport via git URL. Change com.community.netcode.transport.enet to any of the packages in the Transport folder to choose which transport to add:
https://github.com/Unity-Technologies/multiplayer-community-contributions.git?path=/Transports/com.community.netcode.transport.enet

今回はPhoton RealtimeのCommunity Transportsを利用したいので、以下のURLを入力します。サンプルでは少し前のNetcode for GameObjectのバージョンを利用しているため、そのバージョンに対応したCommitIDのリビジョン番号を含めています。

https://github.com/Unity-Technologies/multiplayer-community-contributions.git?path=/Transports/com.community.netcode.transport.photon-realtime#8e87c82deafbac1f73e47c8e2a00d134c51031bd


URLを入力して、【Add】をクリックします。ダウンロードが終わるまでしばらく待ちます。



これでPhoton Realtimeを利用できるCommunity Transportsのインストールができました。簡単ですね!

Photon App Settingsの設定を行う

次はPhoton Realtimeのネットワークを実際に利用するための設定を行います。Photon Cloudが利用できることを前提に記事を書いていますので、下記のように事前に必要な手続きがあります。もし下記内容が事前に済んでいましたらスキップしてもらっても構いません。

事前にしておくこと

あらかじめPhotonのアカウントを作成し、サインインしてダッシュボードに入れるようにしておきます。

id.photonengine.com

以下の画面のようにダッシュボード上にアプリの登録が無い場合、【新しくアプリを作成する】を押します。



Photonの種別には【Realtime】を選択しておき、アプリケーション名の箇所に任意の名前を入れて【作成する】を押します。



作成後、ダッシュボード画面に戻ります。この時先ほど作成したアプリのカードが追加されていると思うので、【アプリケーションID】の横にGUIDの文字列の一部が表示されていますので、そこをクリックします。



クリックするとGUID全体が表示された状態になりますので、Ctrl+Cを押してコピーしておきます。これは後で必要になりますので、クリップボードコピーが心配なら、メモ帳などに一時的に残しておいても良いかもしれません。



これで事前準備完了です。

Photonの設定を変更する

PackageManagerでPhoton RealtimeのCommunity Transportsをインストールした後、Assets配下に Photon/Resources/PhotonSettings.assetというファイルが自動生成されます。このファイルはPhotonの設定を行うもので、今回はPhoton CloudのApp IDを指定します。



App Id Realtimeに取得したPhtonのアプリケーションIDの値を入力し、Fixed Regionにjpを入れておきます。



Transportを差し替える

使用するTransportをPhotonRealtimeを使用するものに変更します。まずはHierarchyからNetworkManagerを選択します。



前に設定したU Net Transportなどのコンポーネントが残ったままになっている場合、先にこのコンポーネントを削除します。コンポーネントを右クリックして、【Remove Component】でコンポーネントを削除します。



NetworkManagerコンポーネントのTransport設定が初期化されますので、【Select transport...】のプルダウンをクリックします。



PhotonRealtimeTransportを選択します。



Photon Realtime Transportのコンポーネントが自動追加されますので、設定します。
RoomNameを指定しておくことでサーバー側で作ったルームと接続先のルームを一致させることができるので、適当な名前を指定しておきます。
また、Batch Modeに【Reliable And Unreliable】に指定することで、MLAPIのイベント処理を効率的に送られるようになります。ネットワークが安定しない場合は【Send All Reliable】を指定しておくことで安定化しますが、遅延などが大きくなる可能性があります。



あとはビルドして試すだけです。立ち上げる際は最初のアプリは【Start as a host】か【Start as a server】を選択してネットワークに参加し、2つめ以降を【Start as a client】にすることでマルチプレイでの参加が可能になります。



最後に

今回はNetcode for GameObjectsのTransportを差し替えることによって、Photon Realtimeのネットワーク上で動作させる内容を扱いました。基本ネットワークエンジンは指定のSDKを使うことが前提となっているため、Netcode for GameObjectsのようにTransportを差し替えて使用できるライブラリは現状少ないかと思います。
開発が進んでくるとライブラリに強く依存するようになるのですが、開発の後半でTransportを差し替えてサードパーティー製のネットワークを切り替えて負荷を見ていくこともできるので、この仕組みはNetcode for GameObjectsを採用するアドバンテージの一つになりそうです。

エンジニアリングマネージャー目線で見るライブラリ技術選定の勘所

技術選定、してますか?

こんにちは!エンジニアリングマネージャーの佐藤(@unsoluble_sugar)です!

今回は開発関連のライブラリやアセットを導入する際に、個人的に見ている技術選定過程のポイントについて書き残してみることにしました。

エンジニアであれば様々な場面で技術選定の判断を求められるかと思います。フロントエンドやサーバサイド、ネットワーク・インフラ構築、CI/CDといった開発領域。OS、言語、フレームワーク、開発ツール、SaaS、プラットフォームetc...

挙げだすとキリがないですよね。

f:id:unsoluble_sugar:20200105164117j:plain

個人開発等でユーザーの母数が小さかったり、運用期間が短く影響範囲が軽微な場合はそれほど気にしなくて良いかもしれません。一方で、企業としてシステム・アプリ開発をする上では、開発して終わりではなく中長期での運用保守が前提となりますので、サービス継続のための様々な点に注意しなければなりません。

対象プラットフォームや開発規模に応じて見るべきポイントは変わるでしょうが、本記事ではライブラリやアセットといった粒度にフォーカスを絞ることにしました。事例を交えつつ、ある程度汎用的に使えそうな技術選定の勘所をまとめています。

「実務経験が浅く、正直どの技術を使えば良いかわからない」といった若手エンジニアさんや「なぜその技術を選んだのか?と問われた際にきちんと説明できるようにしておきたい」という方に、技術選定における指標のひとつとして参考にしていただければ幸いです。

技術選定で見るべき観点

技術選定で見るべき観点として、大枠以下のような要素が挙げられるかと思います。

  • 要件の整理
  • 評価基準の明確化
  • 候補のリストアップ
  • 技術選定
    • 機能面
      • 要求を満たしているか
      • 拡張性
      • 制限事項
    • 導入
      • 実績(導入事例)
        • ネット情報数≒利用者数
      • 継続性
      • イニシャルコスト(学習コスト)
        • サンプルプログラムの質
        • ドキュメントの充実性
        • 扱いやすさ
    • 運用・保守
      • 第三者評価
      • 問い合わせ窓口
      • GitHubリポジトリがあるなら
        • ライセンス形態
        • 開発者のバックグラウンド確認
        • コントリビューター数、スター数
        • 更新頻度
        • issue、プルリク対応の様子
      • セキュリティ
        • メンテナンス、アップデート体制
      • ランニングコスト
        • サブスクリプション型
          • 月額・年額費用の算出
        • 従量課金制の場合
          • APIなら時間帯位でのcall数やトラフィック量
          • SaaSならストレージ使用容量、セッション数、インスタンス起動時間など
      • スイッチングコスト
        • 依存関係
        • 類似ライブラリの候補も挙げておく
          • できれば定期的にウォッチ

これらの要素についてザッと見ていきましょう。

カテゴライズが曖昧だったり話の順序が前後したりしてますが…まぁ大目にみてやってください(予防線

要件の整理

大前提として「何を実現したいか」「何が達成できれば良いのか」を明確にしておく必要があります。システム開発における「要件定義書」にあたる情報がまとまっていると、技術選定も比較的進めやすいですね。

最近ですと、Design Docを採用している組織も多いでしょうか。

www.flywheel.jp

shimo5.me

atmarkit.itmedia.co.jp

キッチリと固まった段階でなくとも、技術選定と並行してこのようなドキュメントを整備していけば、判断基準も自然に定まっていくと思います。

評価基準の明確化

社内リソースで対応する場合であれば、担当メンバーの技術スタック(スキル・前提知識の有無・相談相手が居るか等)、キャッチアップ速度、学習コストをもとに技術選定を進めることが多いでしょうか。

開発期間や予算感によっては、外注できるか否かも判断基準のひとつになりますね。イニシャルコスト、ランニングコストに加えて、スイッチングコストも考慮する必要があります。

例えば利用しているライブラリのサポートが、OSのバージョンアップに追従できず途絶えてしまったり、機能改修が困難になりプラットフォームやサービスそのものがクローズしてしまうといった場合は、否応なく乗り換えが迫られます。

こういったリスクに対する余力はあるか?突発で対応依頼できるプールはあるか?開発速度と安定性のバランスはどう見るべきか?

開発チームに留まらず、経営層・ステークホルダー含めて中長期目線での判断基準を持っておくべきでしょう。

候補のリストアップ

要件を整理し、ある程度の判断基準を設けたら、ざっくり粒度で良いので関連領域においてどのようなライブラリが存在するか情報収集します。いくつかのキーワードの組み合わせでググって、検索上位に出てくる情報数多めなものをリストアップしておけば一旦大丈夫です。

続く技術選定の部分で詳細を辿っていくので、もしここで挙げた候補が見当外れであれば、改めて別のアプローチでリストアップし直せば良いだけですからね。

技術選定

前述の内容と重複する部分もありますが、候補がリストアップできたらライブラリのドキュメントを眺めたり実際に触ってみて、果たして本当に使えるものか見定める段階に入ります。

社内にノウハウや前提知識がない領域では「正直触ってみないとなんも分からん」状態で見積もりが出しづらいため、技術選定には一定の調査期間を設けて取り組むのが無難です。

規模感次第ではありますが、2,3日で終わるものもあれば1ヶ月~数ヶ月要することもあるでしょう。とはいえ仮の期間を設けて注力すれば、「わりとサクッと出来ちゃいそう」「もう少し時間必要かも」「他チームのあのメンバーにも協力して欲しい」とか何となく肌感が掴めます。100%技術調査に時間をかけられるなんてことは稀ですので、そのあたりも鑑みて今後の方針どうするかの議論を進めていくのが建設的ですね。

特にCTO、テックリード、エンジニアリングマネージャーといった役割を担う立場であれば「なぜその技術を選んだのか?」という説明責任を果たす必要があります。エンジニアメンバーからの提案に対しても、よくヒアリングし、気になる点があればディスカッションを重ねた上で慎重に検討していきましょう。

機能面

どの程度要件を満たしているか。パフォーマンスは十分か。拡張性はあるのか。要件の整理工程で洗い出した条件のマッチ具合を見ていきましょう。β版や新しくリリースされたばかりのライブラリは、機能も絶賛開発中で不具合に悩まされることもあります。

不足分は別のライブラリを使用すれば埋められるのか?自作でカバーできる範囲なのか?

一定の実務経験を積めば、過去の開発事例から注意を払わなければならないポイントも見えてきますが、比較的若いエンジニアメンバーで構成された組織ではこのあたり見落としがちです(自分もよく苦労しました)。

導入

流行りに乗って新しいライブラリを導入してみたが、サンプルプログラムの質が微妙だったりドキュメントが整備されていなかったりと「想像以上に学習コストが高かった…」なんてケースもありえます。

大体新しい技術を素早くキャッチアップしてアウトプットしている組織は、レベルの高いエンジニアが居るからこそ実現できているのです。成熟しきっていない技術は導入事例が少なく、参考情報も無く開発が難航することも多々あるでしょう。

一時は熱狂的な盛り上がりを見せていたライブラリが、1,2年後にひっそりと消えてたなんてことはザラにあります。仮に新しいライブラリを導入する際は、担当メンバーや組織にそれ相応の胆力があるかどうかも重要です。

また、忘れてはならないのがライセンスの話です。OSSに関してはここ数年で体系的に学べる書籍が出ているので、開発組織で購入したり社内勉強会等の場で知見共有すると、エンジニアメンバー間での認識も深まることでしょう。

運用保守

技術が好きなエンジニアであれば「最新技術を触ってみたい!」と思うのは至極当然の欲求です。しかしながら、ことビジネスの場においては、ある程度枯れた技術の方が最適な場合もあります。

新しい技術に興味を持って「やってみたい」という人は一定数居る反面、運用保守フェーズに入っているシステムでは即戦力が求められます。何か問題が起きた際の対応に関しては、前提知識がなければトラブルシューティングをこなすことが難しいケースも多いでしょう。

仮に「社内リソースで抱えきれないため外注に出す」という方針になっても、そもそもの母数がなければお願いする相手が居ないという状況が容易に発生します。メインの開発メンバーが離脱してしまうといった場合にも、その穴を埋めるための人員確保が必要です。

技術選定においては、その技術を扱える人材の流動性も考慮しなければなりません。最悪の場合、サービス運用面では至極健全にもかかわらず、開発人員が確保できないためクローズせざるを得ないといった事態に陥るでしょう。

そのような観点では、ライブラリの更新頻度やコントリビューター数、issue、プルリク対応の様子などをしっかり見定めることも大事です。例えばアセットやフレームワークに内包されたライブラリに脆弱性が見つかった際、すぐに対応が施されアップデートで事なきを得られれば「信頼性が高いライブラリ」と言えるでしょう。

逆にいつまで経っても対応の気配が見られず、対象部分がブラックボックス化されており自力での差し替えも難しいといった状況では、セキュリティリスクに晒され続けることになります。

最近はSlackやDiscord上でQ&Aのやり取りが交わされているケースもありますから、コミュニティに飛び込んで活発度を俯瞰するのもアリですね。

事例紹介

ここまでフワッとした持論を展開してきましたが、最後に事例紹介を挙げてお開きとさせていただきます。

先日社内のWebAR関連の技術調査の一環として、Unityで使えそうなARライブラリを調べていました。いくつか候補が挙げられていた中で、自分が調査担当となっていたARWTについての情報を転記しておきます。

github.com

デモ動画だけ見ると「めっちゃ良さげやん」ってなるやつです。

youtu.be

結果的には採用するに至らなかったライブラリとなりますが、技術選定の過程として具体的にどのような部分を見て判断を下したのか、少しでも参考になれば幸いです。

ARWT調査メモ

以下、調査しながら箇条書きで書き残したメモです。

こんな感じで情報収集し、今回は深入りしないことにしました。

蛇足

ちなみにARWT以外の候補として、他メンバーがPlayCanvas8th Wallも見てくれていました。特に8th Wallは、直近でNiantic に買収されたというニュースが飛び込んできて驚きましたね。

大手企業の後ろ盾があると、開発基盤やサポートまわりも確固たるものとなるでしょう。このような最新情報のキャッチアップも、技術選定をする上では欠かせませんね。

まとめ

少数精鋭のスタートアップやベンチャーですと、わりとこの手の判断が個人依存の暗黙知となっていることも多く、開発が佳境に迫った頃に「実は肝心な部分を見落としていた」なんてことも珍しい話ではないでしょう。

そんな悲しい事件を防ぐためにも、エンジニア組織内でこういったノウハウを可視化、テンプレ化しておくとメンバー入れ替えが起きても一定の担保が得られるのではないかと思います。できれば複数人のメンバー目線の意見を突き合わせて、納得感のある技術選定をしていきたいですね。

これ以上の粒度で事細かに書いていくとキリがなさそうだったので、今回は要点を絞って書き連ねてみました。

「こんなところも見た方が良いのでは?」というアドバイスや「この観点の抜け漏れで詰んだ…」等のエピソードがあれば、ぜひコメントやSNSシェア経由で教えていただけると嬉しいです。

最後に

本テックブログやnote記事のお知らせは、Synamon公式Twitterで発信しています。弊社の取り組みに興味を持っていただけましたらぜひフォローお願いします!

twitter.com

カジュアル面談も実施しているので「詳しく話を聞いてみたい!」という方は、こちらもチェックいただけると嬉しいです。

▼カジュアル面談はMeetyから   meety.net  

▼エントリーはこちら   herp.careers

HoloLens2をさわってみた [第4回_AzureSpatialAnchorsを使った空間同期]

はじめに

こんにちは、エンジニアの伊藤(@kazuyaplus)です。
前回はWindowsDevicePortal を使い2台目のホロレンズに、自作アプリをインストールする方法を紹介しました。

第4回ではAzureSpatialAnchorsを使い、2台のホロレンズで同じオブジェクトを同じ場所で共有する方法を紹介していきたいと思います。

第1回から同じプロジェクトを使用しているので、不明な点がある場合は第1回目から確認してみてください。

  • 第1回 : ひとりで使えるHololens2アプリをつくる ~Unity・VisualStudio・MRTKのインストールからビルドまで ~
  • 第2回 : 他の人と使えるHololens2アプリをつくる ~PUN2を使ってオブジェクトを共有~
  • 第3回 : Tips ~Wi-Fiを使ってアプリをインストール~
  • 第4回 : 他の人と同じ空間で使えるHololens2アプリをつくる ~AzureSpatialAnchorsを使った空間同期~

用語集

  • MRTK
    • [Mixed Reality Toolkit]の略で、AR、VRで使えるUIライブラリ
  • PUN2
    • [Photon Unity Networking2]の略で、Unityにオンライン機能を組み込むためのサービス
  • AzureSpatialAnchors
    • 複数のユーザーが同じ物理的な場所にデジタルコンテンツを配置できるようにするためのサービス

検証環境

  • Unity:2020.3.26f1
  • MRTK:2.7.3
  • VisualStudio:2019
  • Device:HoloLens2

手順

シーンの準備

[Hierarchy]で、SharedPlaygroundオブジェクトを展開し、さらにTableAnchorオブジェクトを展開します。

プロジェクトウィンドウで、Assets > MRTK.Tutorials.MultiUserCapabilities > Prefabsフォルダーに移動し、

ButtonsプレハブをTableAnchorにドラッグして、TableAnchorオブジェクトの子としてシーンに追加します。

シーンを操作するためのボタンの構成

次に、AzureSpatialAnchorsを使用するために、ボタンイベントを設定していきます。

[Hierarchy]で、Buttonオブジェクトを展開し、[StartAzureSession]を選択します。

[Inspector]で、Interactableコンポーネントを見つけ、OnClickイベントを次のように設定します。

  • None(Object)フィールドに、TableAnchorオブジェクトを割り当てます。
  • [NoFunction]から、 AnchorModuleScript > StartAzureSessionを選択します。

次に同じ階層内の、[CreateAzureAnchor]を選択します。

同じように、Interactableコンポーネントを見つけ、OnClickイベントを次のように設定します。

  • None(Object)フィールドに、TableAnchorオブジェクトを割り当てます。
  • [NoFunction]から、 AnchorModuleScript > CreateAzureAnchorを選択します。
  • 表示される新しいNone(Object)フィールドに、TableAnchorオブジェクトを割り当てます

次に同じ階層内の、[ShareAzureAnchor]を選択します。

同じように、Interactableコンポーネントを見つけ、OnClickイベントを次のように設定します。

  • None(Object)フィールドに、TableAnchorオブジェクトを割り当てます。
  • [NoFunction]から、 SharingModuleScript > ShareAzureAnchorを選択します。

次に同じ階層内の、[GetAzureAnchor]を選択します。

同じように、Interactableコンポーネントを見つけ、OnClickイベントを次のように設定します。

  • None(Object)フィールドに、TableAnchorオブジェクトを割り当てます。
  • [NoFunction]から、 SharingModuleScript > GetAzureAnchorを選択します。

Azureアカウントを作成する

AzureSpatialAnchorsを使うために、Azureのアカウントをこちらから作成しましょう。

アカウントを作成したら、画像の赤枠で囲った箇所の[Account ID] [Account Domain] [Primary key]をコピーしておきましょう。

シーンをAzureリソースに接続する

[Hierarchy]で、SharedPlaygroundオブジェクトを展開し、TableAnchorオブジェクトを選択します。

[Inspector]で、Spatial Anchor Manager(スクリプト)コンポーネントを見つけます。

事前に作成したAzureSpatialAnchorsアカウントの情報を使用して[Credentials]セクションを設定します。 

  • [Spatial Anchors Account ID]フィールドに、Azure SpatialAnchorsアカウントの[Account ID]を貼り付けます。
  • [Spatial Anchors Account Domain]フィールドに、Azure SpatialAnchorsアカウントの[Account Domain]を貼り付けます。
  • [Spatial Anchors Account Key]フィールドに、 Azure SpatialAnchorsアカウントから[Primary key]を貼り付けます。

[Hierarchy]でTableAnchorオブジェクトを選択し、[Inspector]で[Anchor Module Script]コンポーネントを見つけます。

[Public Sharing Pin]フィールドで、ピンがプロジェクトに固有になるように数桁を変更します。

TableAnchorオブジェクトを選択したまま、[Inspector]で、すべてのスクリプトコンポーネントが有効になっていることを確認します。

一部スクリプトを追加

既に修正されている可能性があるので要確認ですが、この後、紹介する最後の同期手順の7番でGet Azure Anchorボタンを押すと、

DebugWindow で "Local anchor position successfully set to Azure anchor position" と表示され、

デバイス1と同じ位置にオブジェクトが移動するようになっているはずですが、AnchorModuleScript.cs の、

下記の箇所がなぜか抜けているために、正常に動作しなかったので、私の場合は下記部分を追加しました。

AnchorModuleScript.cs

#if WINDOWS_UWP || UNITY_WSA
                // HoloLens: The position will be set based on the unityARUserAnchor that was located.

                // Create a local anchor at the location of the object in question
                gameObject.CreateNativeAnchor();

                // Notify AnchorFeedbackScript
                OnCreateLocalAnchor?.Invoke();

                // On HoloLens, if we do not have a cloudAnchor already, we will have already positioned the
                // object based on the passed in worldPos/worldRot and attached a new world anchor,
                // so we are ready to commit the anchor to the cloud if requested.
                // If we do have a cloudAnchor, we will use it's pointer to setup the world anchor,
                // which will position the object automatically.
                if (currentCloudAnchor != null)  
                {
                    Debug.Log("Local anchor position successfully set to Azure anchor position");

                    //gameObject.GetComponent<UnityEngine.XR.WSA.WorldAnchor>().SetNativeSpatialAnchorPtr(currentCloudAnchor.LocalAnchor);

                    Pose anchorPose = Pose.identity;
                    anchorPose = currentCloudAnchor.GetPose();

                    Debug.Log($"Setting object to anchor pose with position '{anchorPose.position}' and rotation '{anchorPose.rotation}'");
                    transform.position = anchorPose.position;
                    transform.rotation = anchorPose.rotation;

                    //removeAnchor = "Inactive";
                    //Create a native anchor at the location of the object in question
                    gameObject.CreateNativeAnchor();

                    //Notify AnchorFeedbackScript
                    OnCreateLocalAnchor?.Invoke();

                }

WindowsDevicePortal でメッシュをつくる

空間にアンカーを設定する必要があるので、WindowsDevicePortal を開いて設定していきましょう。

WindowsDevicePortalの開き方については、第3回を確認してください。

WindowsDevicePortalを開き、左のメニューから Views > 3D View を開くと下のような画面になっていると思います。

あたりを少し見まわした後、Spatial mapping > Update を押してみましょう、メッシュが生成されると思います。

メッシュが生成されたら、Spatial anchors > Update を押してみましょう、ロケーターが生成されると思います。

同期手順

ここまで出来たら、準備ができましたので、同期してみましょう。

2つのデバイス間でAzure Anchor IDを共有し空間を同じ配置するためには、次の手順のように進めます。

※AzureSpatialAnchorsはUnityでは実行できません。そのため、AzureSpatialAnchorsの機能をテストするには、2つのデバイスにプロジェクトをビルドする必要があります。

  1. デバイス1(ホスト)の場合:アプリを起動します(インスタンス化されたオブジェクトがテーブルに配置されます)
  2. デバイス2(ゲスト)の場合:アプリを起動します(オブジェクトとアバターが表示されますがホストと同じ場所に表示されません)
  3. デバイス1の場合: [Start Azure Session] ボタンを押します。
  4. デバイス1の場合: [Create Azure Anchor ] ボタンを押します。
  5. デバイス1の場合: [Share Azure Anchor] ボタンを押します。
  6. デバイス2の場合: [Start Azure Session] ボタンを押します。
  7. デバイス2の場合: [Get Azure Anchor] ボタンを押します(デバイス1でアンカーが作成された場所にオブジェクトが移動します)

おわりに

これで、2台のHololens2を使い、同じ空間でオブジェクトを共有することができるようになりました。

4週に渡り、Hololens2の基本的なアプリ作成について紹介しました。

基本的にMicrosoftが用意してある、チュートリアル通りではありますが、私自身がつまずいたりした点に関して補足してあります。

このブログが少しでもHololens2ユーザーの助けになればうれしいです。全4回、お疲れさまでした!

参考リンク

docs.microsoft.com

docs.microsoft.com

github.com