見出し画像

UnityでECSを使ってMRアプリを作ってみた

この記事はjig.jp Advent Calendar 2023の12月11日(月)の記事です。


今年はMeta Quest3が発売されましたね。UnityでのMRアプリ開発は、去年と比べて格段に開発しやすくなってきました。
また、Unity 2022 LTSではECS (Entity Component System)が正式サポートされました。
MRアプリでECSを使うことで、処理の高速化が狙えそうです。

では、Quest3向けのMRアプリを作っていきましょう。
Advent Calendarということで、たくさんのキャンディーを降らせるアプリを作ります。

作成するプロジェクトはGitHubのこちらのリポジトリに公開しています。

環境構築

Unity HubからNew Projectを選択します。Unity バージョンは2022LTSにします。
Unity Hubから 3D (URP)で新規プロジェクトを作成します。

必要なパッケージをインストールします。
Assets>Open C# Projectから、プロジェクトをエディタで開きましょう。
manifest.jsonに以下を追記します。

{
  "dependencies": {
    "com.meta.xr.sdk.core": "59.0.0",
    "com.meta.xr.sdk.interaction": "59.0.0",
    "com.unity.entities": "1.0.16",
    "com.unity.entities.graphics": "1.0.16",
    "com.unity.physics": "1.0.16",
  }
}

MRのためのパッケージと、ECSのためのパッケージをまとめて追加しました。
UnityEditorを開くと、自動でパッケージがインストールされます。

ちなみに、メニューの表示はUnity Search機能が便利です。先ほどのメニュー表示や、これから多用するウィンドウ表示を検索して実行できます。

MR開発

プロジェクトの準備ができました。次にQuest3向けの開発をしましょう。
まずはビルド設定を変更します。

ビルド設定

File>Build Settingsを開き、以下の設定をします。

  • プラットフォームは、Androidを選択する

  • Texture Compressionは、ATSCを選択する

設定を変更したら、Switch Platformボタンを押して反映します。

Oculusの設定

他にもOculusの設定が必要です。
Oculusの設定は、自動で修正を適用できます。

Edit>Project Settingsウィンドウを開きます。
OculusタブからFix AllボタンとFix Applyボタンを押して、設定を適用します。
Checklistの内容がすべてVerified Itemsになれば完了です。

パススルー&ハンドトラッキング

続いて、パススルーとハンドトラッキングを有効にしましょう。
Oculus>Tools>Building Blocksウィンドウを開きます。
以下の3つのBlockをクリックして追加します。

  • Camera Rig

  • Passthrough

  • Hand Tracking

追加したCameraRigとMainCameraが競合するため、使用しないMainCameraを削除しておきましょう。
これでパルスルーとハンドトラッキングの設定は完了です。

URP用のマテリアル

ハンドのマテリアルを、URPで表示できるように更新しましょう。
ProjectウィンドウからAssets>Resourcesフォルダで右クリックし、Create>Materialを選択しましょう。
名前をHandMaterialとします。
これでマテリアルが準備できました。

ハンドトラッキングのマテリアルに適用しましょう。
HierarchyウィンドウからHand Trackingオブジェクトを選択します。
InspectorウィンドウからSkinned Mesh Rendererを選択し、作成したマテリアルに変更します。

これでURP用のマテリアルの設定が完了しました。

MRアプリを動かす

アプリを起動してみましょう。
Quest3で起動するため、PCとQuest3を接続します。
File>Build Settingsを開きます。
Run Deviceを接続したQuest3にして、Build And Runボタンを押します。
アプリを起動して、パススルーでハンドトラッキングできることを確認しましょう。

ECS開発

MRアプリの開発は完了です!
次に、ECSの開発をしましょう。

この記事では、ECSの紹介のために説明をかなり省略しました。ECSの説明はこちらの動画 はじめての Unity ECS - Entity Component System を使ってみよう!が分かりやすいです。

シーンの設定

まず、開いているSampleSceneの名前をMainに変更します。
そして、ECSのためのシーンを追加します。Hierarchyを右クリックしてNew Sub Scene>Empty Sceneを選択します。
シーンの名前を"Sub"にします。

Subシーンは、シーンに追加したGameObjectを、Unityが自動でECSのオブジェクトに変換してくれます。

シーンにキャンディーを生成するスポナーを追加しましょう。
Subシーンを右クリックして、GameObject>Create Emptyで追加します。空のGameObjectはSpawnerとしましょう。

Componentを実装

ここからはコードの実装です。Entityを持つComponentを実装します。
ECS(Entity Component System)のEとCですね。

実装する2つのファイルを作成しましょう。
Assets>ScriptsフォルダにConfigAuthoring.csとCandyAuthoring.csファイルを追加します。

名前をAuthoringとしているのは、ECSのComponentをMonoBehaviourからベイクするためです。

ConfigAuthoringの実装

ConfigAuthoring.csに次のコードを追記しましょう。

using Unity.Entities;
using UnityEngine;

public struct Config : IComponentData
{
    public Entity Prefab;
    public int SpawnRadius;
    public int SpawnCount;
}

public class ConfigAuthoring : MonoBehaviour
{
    public GameObject Prefab = null;
    public int SpawnRadius = 10;
    public int SpawnCount = 1000;

    class Baker : Baker<ConfigAuthoring>
    {
        public override void Bake(ConfigAuthoring src)
        {
            var data = new Config()
            {
                Prefab = GetEntity(src.Prefab, TransformUsageFlags.Dynamic),
                SpawnRadius = src.SpawnRadius,
                SpawnCount = src.SpawnCount,
            };
            AddComponent(GetEntity(TransformUsageFlags.None), data);
        }
    }
}

ConfigストラクトがEntityを持つComponentです。
ConfigAuthoringクラスは、Unity EditorからGameObjectに貼り付けられるクラスです。このクラスには、Config Componentの生成方法を記述しています。

CandyAuthroingの実装

続いて、CandyAuthoring.csに次のコードを追記しましょう。

using Unity.Entities;
using UnityEngine;

struct Candy : IComponentData { public float Speed; }

class CandyAuthoring : MonoBehaviour
{
    public float Speed = 3f;

    class Baker : Baker<CandyAuthoring>
    {
        public override void Bake(CandyAuthoring src)
        {
            var data = new Candy { Speed = src.Speed };
            AddComponent(GetEntity(TransformUsageFlags.Dynamic), data);
        }
    }
}

キャンディーそれぞれにSpeed情報を持たせています。

Systemを実装

Systemには、Componentに対する動作を実装します。
Componentと同様に、Assets>ScriptsフォルダにCandySpawnSystem.csとCandySystem.csファイルを追加します。

CandySpawnSystemの実装

CandySpawnSystem.csに次のコードを追記しましょう。
このSystemはConfigに対応します。

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;

[UpdateInGroup(typeof(InitializationSystemGroup))]
public partial struct CandySpawnSystem : ISystem
{
  public void OnCreate(ref SystemState state)
    => state.RequireForUpdate<Config>();

  [BurstCompile]
  public void OnUpdate(ref SystemState state)
  {
    var config = SystemAPI.GetSingleton<Config>();

    var instances = state.EntityManager.Instantiate
      (config.Prefab, config.SpawnCount, Allocator.Temp);
    var rand = new Random(100);

    foreach (var entity in instances)
    {
      var xform = SystemAPI.GetComponentRW<LocalTransform>(entity);

      var v = rand.NextFloat3(-1, 1);
      var size = rand.NextFloat(.3f, .5f);
      xform.ValueRW = LocalTransform
        .FromPosition(math.float3(v.x, v.y, v.z) * config.SpawnRadius)
        .WithScale(size);
    }

    state.Enabled = false;
  }
}

CandySpawnSystemはConfigクラスがあるComponentに対して自動で適用されます。このSystemは、ランダムな位置にキャンディーを生成しています。

CandySystemの実装

CandySystem.csに次のコードを追記しましょう。

using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;

public partial struct CandySystem : ISystem
{
    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        var job = new CandyUpdateJob() { DeltaTime = SystemAPI.Time.DeltaTime };
        job.ScheduleParallel();
    }
}

partial struct CandyUpdateJob : IJobEntity
{
    public float DeltaTime;

    void Execute(in Candy candy, ref LocalTransform xform)
    {
        xform.Position.y -= DeltaTime * candy.Speed;

        if (xform.Position.y < -10f) xform.Position.y = 10f;
    }
}

このCandySystemは、キャンディーを上から下に動かす処理をしています。

キャンディーを用意

Systemで生成し、動かすキャンディーを用意しましょう。
Subシーンを右クリックして、GameObject>3D Object>Sphereを選択しGameObjectを作成します。名前はCandyとします。

キャンディーマテリアルの作成

Assets>Create>Materialを選択しマテリアルを作成します。

マテリアルの色を変更しましょう。
SurfaceInputs>Base Mapから色を設定します。色はキャンディーらしく0x19FF71としました。

次にCandyオブジェクトを選択し、Mesh RendererのMaterialsに作成したマテリアルを適用します。

CandyAuthroingの適用

Candyオブジェクトを選択し、Add ComponentからCandyAuthoringを追加します。

キャンディーのPrefab化

作成したCandyオブジェクトは、Projectウィンドウにドラッグ&ドロップしてPrefabに変換します。

Candyスポナーを設定

Subシーンに追加したSpawnerオブジェクトを設定しましょう。
Spawnerオブジェクトに、Add ComponentからConfig Authoringを追加します。
Config AuthoringのPrefabは、生成したキャンディーPrefabを設定します。

これで設定完了です!

ECSを動かす

MRアプリを起動した手順と同様にアプリを動かしてみましょう。
File>Build Settingsを開き、Build And Runボタンを押します。

動いていますね!

まとめ

Unityでキャンディーを降らせるアプリを作ってみました。
MeshAPIで壁に当たり判定をつけたり、ハンドトラッキングに当たり判定をつけても面白そうですね!

この記事が気に入ったらサポートをしてみませんか?