0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ひとりで完走_C# is GODAdvent Calendar 2024

Day 22

UnityECS MonoBehaviourからイベントやデータを送る方法

Posted at

MonoBehaviourとの連携

UnityECSは素晴らしいフレームワークですが、UnityECSは現行のすべてのUnityの機能を完全には扱うことはできません。
前回から行っているAnimationについても既存のMonoBehaviourと連携しHybridアプローチを行う必要があります。

GameObjectとEntityを紐づけ

例えばMonoBehaviour側が何かイベントを発行してそれに対してECSで処理をしたい場合を考えてみましょう。
MonoBehaviourからECS側に何か送る場合はあらかじめComponentに送りたいデータを提示して、SetComponentから送る方法やAddComponentでタグをつけて、タグが付いていたらそのSystemで処理する方法があります。

いずれにしてもこのGameObjectに紐づけられているEntityを知ることが必要です。
まずGameObjectにEntityを格納するためのEntityHolderコンポーネントを作成し、アタッチします。

public class EntityHolder : MonoBehaviour
{
    public Entity MyEntity;
}

単純にEntityを保持するだけのクラスです。

アタッチされたGameObject
スクリーンショット 2024-12-22 104115.png

次にECS側からGameObjectに紐づけます。
これは以前PlayerHybridSystemでやったことの逆をすればいいわけです。

protected override void OnUpdate()
{
    EntityCommandBuffer ecb = SystemAPI.GetSingletonRW<EndSimulationEntityCommandBufferSystem.Singleton>().ValueRW.CreateCommandBuffer(World.Unmanaged);

    //Create
    foreach (var (hybridData, entity) in 
        SystemAPI.Query<CharacterHybridData>()
                 .WithNone<CharacterHybridLink>().WithEntityAccess())
    {
        // AuthoringでアタッチされたGameObjectを実体化する。
        GameObject tmpObject = GameObject.Instantiate(hybridData.MeshPrefab);
        Animator animator = tmpObject.GetComponent<Animator>();
       // その実体化されたGameObjectの`EntityHolder`を取得し
+       EntityHolder holder = tmpObject.GetComponent<EntityHolder>();
+       // entityを入れてやればよい
+       holder.MyEntity = entity;

        // こちらは以前やったもので、Entity側にGameObjectを紐づけている
        ecb.AddComponent(entity, new CharacterHybridLink
        {
            Object = tmpObject,
            Animator = animator,
        });
        ecb.AddComponent<CharacterHybridData>(entity);
    }
    ecb.Dispose();
}

ECS側のAuthoringの写真も一応載せます。
スクリーンショット 2024-12-22 104427.png
AuthoringのCharacter AuthoringMesh Prefabに先ほどアタッチしたGameObjectのプレファブを入れるわけです。

MonoBehaviourでAddComponent,SetComponent

ここまでくればあとはMonoBehaviour側でAddComponentSetComponentをやってしまえばいいわけです。

例えばメカニムのStateMachineBehaviourを使って呼び出してみる

public class AttackStateBehaviour : StateMachineBehaviour
{
    Entity entity;
    //OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        entity = animator.transform.GetComponent<EntityHolder>().MyEntity;
        if(entity != null)
        {            
            var manager = World.DefaultGameObjectInjectionWorld.EntityManager;
            // AddComponentでタグをつける
            manager.AddComponent<AttackState>(entity);
        }
    }

    // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
    //override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    //{
    //    
    //}

    //OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        entity = animator.transform.GetComponent<EntityHolder>().MyEntity;
        if(entity != null)
        {            
            var manager = World.DefaultGameObjectInjectionWorld.EntityManager;
            // AddComponentでタグをつける
            manager.AddComponent<NormalState>(entity);
        }
    }

    // OnStateMove is called right after Animator.OnAnimatorMove()
    //override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    //{
    //    // Implement code that processes and affects root motion
    //}

    // OnStateIK is called right after Animator.OnAnimatorIK()
    //override public void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    //{
    //    // Implement code that sets up animation IK (inverse kinematics)
    //}
}

これはメカニムのステートマシーンに入ったとき対象のEntityにAttackStateというタグをつけて、ステートマシーンから出たときにNormalStateというタグをつけているサンプルになります。
GameObject側からEntityManager.〇〇Componentを呼ぶ場合はWorld.DefaultGameObjectInjectionWorld.EntityManagerEntityManagerを取得することで呼ぶことができます。

それぞれのStateの処理をECSのSystemで行っています。
SetComponentの例ものせておきます。

public class AttackStateBehaviour : StateMachineBehaviour
{
    Entity entity;
    //OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        entity = animator.transform.GetComponent<EntityHolder>().MyEntity;
        if(entity != null)
        {            
            var manager = World.DefaultGameObjectInjectionWorld.EntityManager;
            // GetComponentで現在のコンポーネントの値を取得
            PlayerInputs inputs = manager.GetComponentData<PlayerInputs>(entity);
            // データを書き換えて
            inputs.FirePressed = true;
            // 再設定する
            manager.SetComponentData(entity, inputs);
        }
    }

    //OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        entity = animator.transform.GetComponent<EntityHolder>().MyEntity;
        if(entity != null)
        {            
            var manager = World.DefaultGameObjectInjectionWorld.EntityManager;
            // 現在のコンポーネントのデータを参照しなくてもいい場合は新しくコンポーネントを作成しセットしてもいい
            // 同じコンポーネントにデータがある場合はそれも初期化されるので注意
            manager.SetComponentData(entity, new PlayerInputs
            {
                FirePressed = true,
            });
        }
    }
}

これでUnityECSとMonoBehaviourの連携をうまくすることができるようになります。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?