LoginSignup
1
0

Unity DOTSで当たり判定を取得したい

Last updated at Posted at 2022-12-30

追記と注意(2023 / 5 / 20)

Entities 1.0.0-pre65で同じ操作をしたところ、EntityCommandBufferがJobで取得できないという問題に遭遇しました。解決方法は確認できていません。
そのためコードに関しては動かない可能性が高いです。

Unity DOTS 4日目

ただの大学生がUnity DOTSを頑張る。4日目です。今日も極力嘘書かないように頑張ります。

環境

Unity 2022.2.1f1
Entities 1.0.0-pre.15
Burst 1.7.4
Unity Physics 1.0.0-pre.15

今日の内容

昨日の宣言通り、当たり判定の処理を作ったりEntityに力を加えたりします。

そもそもUnity Physicsとは

「Unity Physics」はUnityがUnity DOTS用に提供している物理シミュレーション用のパッケージですね。

Unity DOTS用には「Havok Physics for Unity」というパッケージも提供されています。
これはHavok社が開発した物理シミュレーションをUnityで使えるようにしたものだとか。
しばらくしたら、これについての記事も書きたい。

前準備

Unity Physicsのパッケージを入れよう!
んで、お馴染みの3Dオブジェクトを配置します。
からの、いつもの○○ ColliderをRemove
そして、Physics Shapeをアタッチします。

Physics ShapeとPhysics Body

今までのBox ColliderとかCapsule ColliderのUnity Physics版がPhysics Shapeになります。
そして、RigidbodyのUnityPhysics版がPhysics Bodyになります。

普通にColliderとかRigidbodyとかでも機能はしました。
がそれ以上のことは確認してないので普通にPhysicsで提供されるコンポーネントを使いましょう。

(2023/5/31 追記)
最新版では、通常のBox Cliderの使用が推奨されているようです。詳しくは下のチェンジログに書いてあります。

スクリプトの話

余談ですが、ここから大変長い旅が始まります。
私はこの解決方法を見つけるまで10時間ぐらいは使ってます。本当にありがとうございます。

接触判定を取る。

まず、1つ言いたい。ここ、公式ドキュメント間違ってるんよ!

適当コード

using Unity.Burst;
using Unity.Entities;
using Unity.Jobs;
using Unity.Physics;
using Unity.Physics.Systems;
using UnityEngine;

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(PhysicsSimulationGroup))]
[BurstCompile]
public partial struct CubeSystem : ISystem
{
    public void OnCreate(ref SystemState state)
    {

    }

    public void OnDestroy(ref SystemState state)
    {
        
    }

    public void OnUpdate(ref SystemState state)
    {
        state.Dependency = new DestroyCubeJob
        {
            _cubes = state.GetComponentLookup<CubeData>(),
            _players = state.GetComponentLookup<Player>(),
            _ecb = state.World.GetOrCreateSystemManaged<BeginSimulationEntityCommandBufferSystem>().CreateCommandBuffer()
        }.Schedule(SystemAPI.GetSingleton<SimulationSingleton>(), state.Dependency);
    }
    [BurstCompile]
    public partial struct DestroyCubeJob : ICollisionEventsJob
    {
        public ComponentLookup<CubeData> _cubes;
        public ComponentLookup<Player> _players;
        public EntityCommandBuffer _ecb;
        public void Execute(CollisionEvent _collisionEvent)
        {
            var _entityA = _collisionEvent.EntityA;
            var _entityB = _collisionEvent.EntityB;

            var _isEntityACube = _cubes.HasComponent(_entityA);
            var _isEntityBCube = _cubes.HasComponent(_entityB);

            var _isEntityAPlayer = _players.HasComponent(_entityA);
            var _isEntityBPlayer = _players.HasComponent(_entityB);

            if (_isEntityACube && _isEntityBPlayer)
            {
                _ecb.DestroyEntity(_entityA);
            }
            else if (_isEntityAPlayer && _isEntityBCube)
            {
                _ecb.DestroyEntity(_entityB);
            }
        }
    }
}

上記のスクリプトは、PlayerCubeDataという自作コンポーネント(IComponentData)を持つEntityが接触したときにCubeEntityをDestroyするものです。

解説

ざっと大まかな流れを説明します。

1.OnUpdate()で事前にC# JobSystemのJobをスケジュールしておきます。
2.接触するとDestroyCubeJobのExecuteが発火します。

あら簡単。

公式ドキュメントとの違い

公式ドキュメントは、スケジュールする関数の引数が1個しか指定してませんでした。
しかし、それではVisual Studioくんが怒ってきます。

// 公式ドキュメントの書き方
.Schedule(SystemAPI.GetSingleton<SimulationSingleton>());
// 実際に動いた書き方(Ver. ISystem)
.Schedule(SystemAPI.GetSingleton<SimulationSingleton>(), state.Dependency);
// 実際に動いた書き方(Ver. SystemBase)
.Schedule(SystemAPI.GetSingleton<SimulationSingleton>(), Dependency);

引数の中身に関してはおまじないだと思ってます。いずれ理解するときが来るでしょう。知らんけど

Executeの中身

まず、_collisionEventという変数にはイベント発火の原因となった2つのEntityが格納されています(EntityA, EntityB)

少し検証しましたが、どのようにA, Bが決まるのかは法則がありそうでした。
それがどんな法則かまでは調べてません。

それら2つのEntityがどういったコンポーネントを持っているのか
もう少し言えばどういったタグ付けがされているかHasComponentメソッドで判定します。
_cubes.HasComponent(_entityA);は_entityAはキューブですか?といった感じです。

ちなみに、_cubesはSystemのstate.GetComponentLookup<CubeData>()で取得しています。
OnUpdate()の中でGetComponentLookup<T>()を使うとUnityに注意を受けるのですがそれ以外の方法を見つけることができなかったです。

そのあとは簡単ですね、Aがキューブ、BがプレイヤーならAを削除。
Bがキューブ、AがプレイヤーならBを削除といった具合に処理を書いているだけです。
詳しいEntityの消し方はこちら

完成!やったね!

スクリプトが書けました!接触させてテストしましょう!
うん、反応しない!!!
公式ドキュメントには見当たらなかったのですが、Physics Shapeの設定を変えないと発火しません

そもそもイベントの種類

先ほどのコードではICollisionEventsJobというインターフェースでJobを作成しました。
しかし、もう1種類接触判定を受け取れるインターフェースがあります。
それがITriggerEventsJobです。

Physics Shapeの設定

Physics Shapeのコンポーネントの画面。
image.png
これの、MaterialのCollision Responseですね。
これを変えないといけません。
image.png
初期設定はCollideです。ただの壁ですね。
Collide Raise Collision Eventsはただの壁兼イベント発火も検知するマンです。
Raise Trriger Eventsは壁として機能しなくなり、イベント発火しか検知しなくなります。

お分かりかもしれませんが、後者2つがそれぞれインターフェースに対応しています。
Collide Raise Collision EventsICollisionEventsJobのイベントを発火できます。
Raise Trriger EventsITrigerEventsJobのイベントを発火できます。

やったね。動くよ!

お疲れ様でした。
コンポーネントの設定という問題にたどり着くのに普通に数時間かかりました。
おかげで、横目で見てたマーベル作品が2個ぐらい終わりましたね。。。

最後に

プレイヤーをジャンプさせるとこまで書こうとおもったのですが、思ったり文量が多くなったのでやめます。
いずれ、気分が乗ったら書きます。
たぶん明日。
明日は「Unity Physics」or「Havok Physics for Unity」の記事を書こうと思います。
雑記にお付き合いいただきありがとうございました。また明日。良い大晦日を…

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