見出し画像

Unity DOTS 入門 (11) - トリガーイベント

Unity Physics」による「トリガーイベント」の使ってみます。

・Unity 2019.4.5f1
・Entities 0.11.1
・Hybrid Renderer 0.5.2
・Unity Physics 0.4.0

1. DOTSパッケージのインストール

DOTSパッケージのインストール手順は、次のとおりです。

(1) メニュー「Window → Package Manager」でPackage Managerを開く。
(2) ウィンドウ上部の「Advances → show preview packages」をチェック。
(3) 「Unity Physics」(0.4.0)を検索してインストール。
(3) 「Hybrid Renderer」(0.5.2)を検索してインストール。

これによって、「Entities」が依存関係として追加され、依存する関連パッケージ(BurstCollectionsJobsMathematicsなど)が再帰的に追加されます。

1. Unity Physicsによるトリガーイベント

Unity DOTS 入門 (9)」のサンプルを改良して、「Unity Physics」による「トリガーイベント」を試してみます。

(1) 「Unity DOTS 入門 (9)」のサンプルの「PlayerTag」のないCubeを1つだけ残し、名前を「Item」とし、Transformを以下のように設定。

Translate = (-2, 0, 0)
Rotation = (0, 0, 0)
Scale = (0.8, 0.8, 0.8)

画像1

(2) Hierarchyウィンドウで「Item」を選択し、Inspectorウィンドウの「Add Component」で「Physics Body」(従来のRigidBody)を追加し、「Motion Type」で「Kinematic」を選択。

画像2

「Motion Type」(動作種別)の種類は、次のとおり。

Dynamic : 物理演算で動作(移動、回転、表示ON/OFF)
Kinematic : スクリプトで動作
Static : 原則動作せず、まれに動作
Physics Bodyなし : 動作しない

(3) Hierarchyウィンドウで「Item」を選択し、Inspectorウィンドウの「Add Component」で「Physics Shape」(従来のBoxColider等)を追加し、「Collision Rensponse」で「Raise Trigger Events」を選択。

画像4

「Collision Response」(衝突応答)の種類は、次のとおりです。

Collide : 衝突あり
Collide Raise Collision Event : 衝突イベント
Raise Trigger Event : トリガーイベント
None : 衝突なし

(4) Hierarchyウィンドウで「Cube」を選択し、Inspectorウィンドウの「Add Component」で「Physics Body」(従来のRigidBody)を追加し、「Motion Type」で「Kinematic」を選択。

(5) Hierarchyウィンドウで「Cube」を選択し、Inspectorウィンドウの「Add Component」で「Physics Shape」(従来のBoxColider等)を追加し、「Collision Rensponse」で「Collide」を選択。

(6) 「Item」にスクリプト「ItemTag」を追加。

using Unity.Entities;

[GenerateAuthoringComponent]
public struct ItemTag : IComponentData
{
}

(7) Projectウィンドウにスクリプト「ItemTriggerSystem」を追加。

using UnityEngine;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Burst;
using Unity.Collections;
using Unity.Physics;
using Unity.Physics.Systems;

[UpdateAfter(typeof(EndFramePhysicsSystem))]
public class ItemTriggerSystem : SystemBase
{
    private BuildPhysicsWorld buildPhysicsWorld;
    private StepPhysicsWorld stepPhysicsWorld;

    private EndSimulationEntityCommandBufferSystem commandBufferSystem;

    protected override void OnCreate()
    {
        base.OnCreate();

        // ワールドの取得
        buildPhysicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
        stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();

        // コマンドバッファシステム
        commandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }

    protected override void OnUpdate()
    {
        // ジョブの生成
        var job = new ItemTriggerSystemJub();
        job.allItems = GetComponentDataFromEntity<ItemTag>(true);
        job.allPlayers = GetComponentDataFromEntity<PlayerTag>(true);
        job.entityCommandBuffer = commandBufferSystem.CreateCommandBuffer();

        // ジョブの実行
        Dependency = job.Schedule(
            stepPhysicsWorld.Simulation,
            ref buildPhysicsWorld.PhysicsWorld,
            Dependency);

        // 完了すべきジョブをコマンドバッファシステムに追加
        commandBufferSystem.AddJobHandleForProducer(Dependency);
    }
    
    struct ItemTriggerSystemJub : ITriggerEventsJob
    {
        [ReadOnly] public ComponentDataFromEntity<ItemTag> allItems;
        [ReadOnly] public ComponentDataFromEntity<PlayerTag> allPlayers;

        public EntityCommandBuffer entityCommandBuffer;

        public void Execute(TriggerEvent triggerEvent)
        {
            Entity entityA = triggerEvent.EntityA;
            Entity entityB = triggerEvent.EntityB;

            // アイテムとアイテムの交差
            if (allItems.Exists(entityA) && allItems.Exists(entityB))
            {
                return;
            }

            // プレイヤーとアイテムの交差
            if (allItems.Exists(entityA) && allPlayers.Exists(entityB))
            {
                // アイテムの破棄
                entityCommandBuffer.DestroyEntity(entityA);
            }
            else if (allPlayers.Exists(entityA) && allItems.Exists(entityB))
            {
                // アイテムの破棄
                entityCommandBuffer.DestroyEntity(entityB);
            }
        }
    }
}

◎ BuildPhysicsWorld と StepPhysicsWorld と ExportPhysicsWorld
Unity Physicsは、システムを異なるWorldで管理化します。

BuildPhysicsWorld : 準備
StepPhysicsWorld : シミュレーション
ExportPhysicsWorld : 最終結果

画像5

今回は、「BuildPhysicsWorld」と「StepPhysicsWorld」を、「ジョブ」(ITriggerEventsJob)の実行に利用します。

EntityCommandBuffer
「エンティティの生成・破棄」「コンポーネントの追加・削除」「共有コンポーネントの値の変更」といった操作は、チャンク構造を変更してしまうため、ForEachのラムダ関数では実行できません。このような操作を行いたい場合は、「EntityCommandBuffer」を使って操作を遅延実行させます。

ビルトインのEntityCommandBufferSystemがいくつか定義されています。

・BeginInitializationEntityCommandBufferSystem
・EndInitializationEntityCommandBufferSystem
・BeginSimulationEntityCommandBufferSystem
・EndSimulationEntityCommandBufferSystem
・BeginPresentationEntityCommandBufferSystem

今回は、「EndSimulationEntityCommandBufferSystem」を使っています。

画像6

◎ EndSimulationEntityCommandBufferSystem 
(1) EndSimulationEntityCommandBufferSystemの準備。

commandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();

(2) ジョブ実行時にコマンドバッファを生成。

job.entityCommandBuffer = commandBufferSystem.CreateCommandBuffer();

(3) 完了する必要があるジョブをコマンドバッファシステムに追加。
コマンド実行前に、完了する必要があるジョブをコマンドバッファシステムに追加しています。

commandBufferSystem.AddJobHandleForProducer(Dependency);

(4) ジョブ内でのエンティティの破棄。

entityCommandBuffer.DestroyEntity(entityA);
entityCommandBuffer.DestroyEntity(entityB);

(8) 実行。
「Cube」を操作して「Item」と衝突した時、「Item」が消えることを確認します。

画像4


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