【Unity】Unity ECSでRaycastを飛ばしたい。

# 自己紹介
そこら辺の工業大学に通う大学生です。

# 今回やること

https://qiita.com/AtsuAtsu0120/items/707fea9827cbee1d6585

上の記事の続編（？）です。この記事ではオブジェクト周辺に当たり判定を作成してそこにPlayerがぶつかれば色を変えるということをしていました。しかし、アイテムのハイライトとして使うには正面を向いているとき以外でも反応してしまうのはあまり良いとは言えないでしょう。

ということで、Raycastを使ってやってみます。
## 環境
Unity 2022 LTS(2022.3.0f1)
>ついにきましたね。LTS…！正式名称はUnity 2022.3 LTSではなく Unity 2022 LTSらしいですよ。

Entities 1.0.10
Entities Graphics 1.0.10
Physics 1.0.10


# サンプルコード

```C#:RaycastSytem.cs
using AtsuAtsu.Components.Player;
using AtsuAtsu.Components.SpawnButton;
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Systems;
using Unity.Transforms;
using UnityEngine;

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateAfter(typeof(PhysicsSystemGroup))]
[BurstCompile]
public partial struct RaycastSystem : ISystem
{
    RaycastInput input;
    PhysicsWorldSingleton physics;

    public void OnCreate(ref SystemState state)
    {
        var filter = new CollisionFilter()
        {
            GroupIndex = 0,
            BelongsTo = 1 << 1,
            //レイヤー6（Player自身）だけ反応しないようにする。
            CollidesWith =  ~(~ 1u << 5 & 1u << 6)
        };
        Debug.Log($"{filter.CollidesWith}");
        input = new RaycastInput
        {
            Start = new float3(0, 0, 0),
            Filter = filter,
            End = new float3(0, 0, 0)
        };
    }
    public void OnDestroy(ref SystemState state)
    {

    }
    public void OnUpdate(ref SystemState state)
    {
        physics = SystemAPI.GetSingleton<PhysicsWorldSingleton>();

        //Raycastを飛ばすオブジェクトを取得
        var player = state.GetEntityQuery(typeof(PlayerTag)).GetSingletonEntity();

        //向きと座標情報を取得するためにLocalToWorldを取得
        var localToWorld = state.EntityManager.GetComponentData<LocalToWorld>(player);

        input.Start = localToWorld.Position;

        //終点を正面になるように計算
        var distance = 5;
        var goal = localToWorld.Forward * distance;
        input.End = goal + localToWorld.Position;

        //レイを出して最寄りのオブジェクトを取得
        if (physics.CastRay(input, out var hit))
        {
            var name = state.EntityManager.GetName(hit.Entity);
            Debug.Log(name);
            state.EntityManager.AddComponent<ChangeOutlineTag>(hit.Entity);
        }
    }
}
```
コードだけ読んだらなんとかなる人は上をどうぞ。プレイヤーからRaycastを飛ばしています。

## GameObjectと何が違うの？
正直、DOTSを触れている人でGameObjectでRaycast触ったことない人はいないと思うのでその差を書けばなんとかなるでしょう。
### 使う引数が違います。
GameObjectのほうは始点と向きと距離で飛ばしていたと思うのですが、なぜかECSのPhysicsでは`start`と`end`つまり、始点と終点を与えてその間にレイを飛ばすらしいです。

そのため、少し終点を求める式を書く必要がありますね。ベクトルの計算です。

### その他違う点
あとは、一度RaycastInputを経由して引数に与えたりすることぐらいですかね。

# Boxなどその他のRaycast
ここで朗報なのか悲報なのか…
Boxなどほかの形はなんと、従来のままです。終点の計算いりません。
どうしてこうなった…

# CollisionFilter
これもGameObjectにあった概念ではありますが少し復習というか、私があまり理解していなかったので…
この構造体の変数に関して記載していきます。

## GroupIndex
これは基本的には0で大丈夫でしょう。0が普通のグループになるようです。
## BelongTo
レイ自身が所属するレイヤーです。これはよく分かりません。
次の`CollidesWith`で当たるレイヤーを設定するのになぜこれがいるのか…？
## CllidesWith
ここが一番大事です。これはビット演算子と呼ばれる変数らしいです。
例えばレイヤー0番「Default」のみぶつかるようにするには`0001`、レイヤー0と2のみぶつかるには`0101`などとします。
※本当はもっと桁数大きいですが、便宜上4bitで表してます。

下のケタからそれぞれレイヤー番号と対応しており、1のレイヤーはぶつかる。0のレイヤーはぶつからないということです。
私も深く理解していないのでうまく表せているか不安ですが、まぁそういうことです。

# AddComponent
そのあとはこのアウトラインを出してねーというタグをエンティティに付けてほかのシステムで処理してもらってます。
しかし、ほかのシステムのほうが良くないのか、アウトラインが点滅してしまう問題があるので修正したいです。

（追記）
同じシステム内でアウトラインをつける処理を行ったら解決しました。
原因としては、アウトラインをつけるシステムのほうが処理が軽く回数が多く回っていたようです。

システムを細分化しすぎるのも良くないかもしれませんね。
# さいごに
RaycastをC# JobSystemで動かすことができるらしいのでやりたかったんですが、私の理解度が足りておらずJob化できませんでした。やり方わかる人いましたらぜひ教えてください。

さて、これからも色々遊んでいきますよ！

最近GitHubに草を生やせそうなプログラムを書いたらpublicで公開しようと思っているのでたまに見に来てくれたらうれしいです。
ではまた。