LoginSignup
2
2

【Unity】EntityとGameObjectを連携したい

Posted at

自己紹介

明後日(5/26)にゲームジャムを控え、大興奮が止まらない大学生

記事の参考動画

DOTS使ってますか?

Unity DOTS楽しいですよね。この前、Ver.1.0.8がリリースされてついにExperimentalが外れましたね。
一方で、機能が足りないところはしばしば…

今回は

今回は、機能が足りないところを既存のGameObject関連の機能と連携させてみたいと思います。
簡易的なゲームにも必須なUI(Unity UI)を連携させますが、ほとんど同じです。

タイトルは分かりやすく、EntityとGameObjectの連携と書きましたが、正確にはMonoBehaviourとECSの連携といったほうが正確かもしれません。

MonoBehaviour → ECS

これは非常に簡単に行えます。

MonoBehaviour側

Button.cs
public class Button : MonoBehaviour
{
    private EntityManager entityManager;
    private Entity entity;
    private async void OnEnable()
    {
        entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;

        await UniTask.Delay(100);
        //Entityを取得
        entity = entityManager.CreateEntityQuery(typeof(Spawner)).GetSingletonEntity();
    }

    public void OnButtonClick()
    {
        entityManager.AddComponent(entity, typeof(CharacterSpawnTag));
    }
}

このOnButtonClickをuGUIから呼び出せば、EntityにComponentを追加しそれをSystemで拾ってあげれば…連携完了!

一方で、この方法ではMonoBehaviourから数値などを含むコンポーネントを追加できない問題があります。
このAddComponentBaker<T>を継承したクラスで利用できるものとは異なるため、数値などを渡す方法は異なります。その方法はAddComponentしたのち、SetComponentメソッドで変更できます。
なぜ、直接的に指定できないのかは不明です。もしかしたら、直接指定できるのかもしれませんが、私が調べた限りは無理でした。

スクリプトの詳細解説

まずは7行目、Entityを管理するEntityManagerを取得します。
これを利用してEntityにコンポーネントを張り付けていきます。

entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;

次にEntityManagerから対象となるEntityを取得します。
今回はSpawnerというコンポーネントがついているEntityを1つ取得しています。
このGetSingletonEntityメソッドでは対象となりうるEntityが複数個あるとエラーとなりますので気を付けてください。

その前に、UniTaskという外部のパッケージで少し遅延を発生させています。
これは、ECSの初期化が完了してからEntityを取得しなければエラーとなるからです。

   await UniTask.Delay(100);
   //Entityを取得
   entity = entityManager.CreateEntityQuery(typeof(Spawner)).GetSingletonEntity();

最後に、OnButtonClickからAddComponentで取得したEntityにコンポーネントをつけます。
今回はCharacterSpawnTagというコンポーネントデータを作成しEntityに張り付けています。

ECSの設定

CharacterSpawnSystem.cs
[BurstCompile]
public partial struct CharacterSpawnSystem : ISystem
{
    private void OnCreate(ref SystemState state)
    {
        
    }
    private void OnDestroy(ref SystemState state) 
    {

    }
    public void OnUpdate(ref SystemState state)
    {
        var ecb = new EntityCommandBuffer(Allocator.Temp);
        foreach(var (_, characterObj, entity) in SystemAPI.Query<CharacterSpawnTag, CharacterGameObjectsData>().WithEntityAccess())
        {
            //何か処理を書く
            ecb.RemoveComponent<CharacterSpawnTag>(entity);
        }
    }
}

こちらがECS側のスクリプト(一部略)です。
内容は、EntityCommandBufferを取得しそのEntityを使って何かを行い、その後MonoBehaviour側で指定したコンポーネントを外せば一度だけ処理されるということになりました。
今回はボタンを利用しましたが、それ以外のUnityEventとも連携出来ることになります。

ECS → MonoBehaviour

こちらは、MonoBehaviourのActionをECSから呼び出すというイメージです。

ECS側の設定

ECSToMono.cs
using System;
using Unity.Entities;

public partial class ECSToMono : SystemBase
{
    public Action<int> action;
    protected override void OnUpdate()
    {
        action?.Invoke(100);
    }
}

このようにActionを定義し、ECS側で発火(Ivoke)させます。今回はOnUpdateの中で発火していますが、このようなことはあまりないかもしれません。
また、Actionのジェネリクスは指定するメソッドの引数です。

次はMonoBehaviour側のスクリプトです。

ECSToMonoReceiver.cs
using Unity.Entities;
using UnityEngine;

public class ECSToMonoReceiver : MonoBehaviour
{
    private void OnEnable()
    {
        var system = World.DefaultGameObjectInjectionWorld.GetExistingSystemManaged<ECSToMono>();
        system.action += TestAction;
    }
    private void OnDisable()
    {
        var system = World.DefaultGameObjectInjectionWorld.GetExistingSystemManaged<ECSToMono>();
        system.action -= TestAction;
    }
    public void TestAction(int i)
    {
        Debug.Log(i);
    }
}

ここのキーポイントは、システムの取得方法です。
World.DefaultGameObjectInjectionWorld.GetExistingSystemManaged<T>()でsystemを取得できます。
ここから先ほど書いたactionというフィールドにアクセスし関数を代入します。
なお、これはISystemではMonoBehaviourから取得することが出来なさそうだったのでSystemBaseを使う必要がありそうです。もし、ISystemで取得できそうだったら教えてほしいです。

これで、コンソールに100と表示されます。
ECS→MonoBehaviourの連携ができました!

まとめ

MonoBehaviourからECSは

  • コンポーネントをMonoBehabiourから設定
  • それをSytemで検知しデータを取得

という手順でした。

ECSからMonoBehaviourは

  • Action型の変数をSystemに定義
  • そのAction型の変数にMonoBehaviourに定義したメソッドを代入

という手順です。

いい感じに設定できましたね。やったね。

さいごに

ほかにもDOTS(主にECS)の記事を書いていますので良かったら見てください。
ゲームジャム頑張るぞー!!

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2