追記と注意(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);
}
}
}
}
上記のスクリプトは、Player
とCubeData
という自作コンポーネント(IComponentData)を持つEntityが接触したときにCube
Entityを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のコンポーネントの画面。
これの、MaterialのCollision Responseですね。
これを変えないといけません。
初期設定はCollide
です。ただの壁ですね。
Collide Raise Collision Events
はただの壁兼イベント発火も検知するマンです。
Raise Trriger Events
は壁として機能しなくなり、イベント発火しか検知しなくなります。
お分かりかもしれませんが、後者2つがそれぞれインターフェースに対応しています。
Collide Raise Collision Events
はICollisionEventsJob
のイベントを発火できます。
Raise Trriger Events
はITrigerEventsJob
のイベントを発火できます。
やったね。動くよ!
お疲れ様でした。
コンポーネントの設定という問題にたどり着くのに普通に数時間かかりました。
おかげで、横目で見てたマーベル作品が2個ぐらい終わりましたね。。。
最後に
プレイヤーをジャンプさせるとこまで書こうとおもったのですが、思ったり文量が多くなったのでやめます。
いずれ、気分が乗ったら書きます。
たぶん明日。
明日は「Unity Physics」or「Havok Physics for Unity」の記事を書こうと思います。
雑記にお付き合いいただきありがとうございました。また明日。良い大晦日を…