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

【Unity DOTS】ECSでEntityのMaterialのプロパティーを変更したい

Last updated at Posted at 2025-05-01

■概要

EntityのMaterialのプロパティーをコードから変更する。
(DOTS初学者なのでコードとか諸々:poop:かも)

■やること

  1. HybridRendererの導入
  2. ShaderGraphでMaterialを作成
  3. PrefabからEntityを何個か作成して、それらのMaterialのプロパティーをSystemクラスからいじる
  4. 猫の腹を撫でる

■サンップル

sample3.gif

■環境

UnityEngine : 6000.0.23f1
Entities : 1.3.2
Entities Graphics : 1.3.2
Hybrid Renderer : 0.5.2-preview.4
猫 : 3歳

■リファレンス

↓ECSで変更したいMaterialの設定

↓コードでMaterial作る場合に必要そうなやつ。
ShaderGraphで作るならいらない。

■導入

Entities、Entities Graphicsはパッケージマネージャーから入れられる。

Entitiesより先にEntities Graphicsを入れたほうが良いです。
Entities Graphicsを入れると、同時に同バージョンのEititiesも追加されます。

しかし、現状HybridRendererはパッケージマネージャーから検索しても出てこない。
なので直接manifest.jsonに書き込む。
(プロジェクト名/Packages/manifest.json)

"com.unity.rendering.hybrid": "0.5.2-preview.4",

(この時点で赤文字がいっぱいでてきたりするけど怖いからClear。レッチュゴー)

■マテリアルの作成

今回はMaterialをShaderGraphで作成してます。

コードで作る場合は、ShaderGraphではデフォルトで設定されている部分も書く必要があるらしい。

今回は適当にColor,float,Vector4(*1)のプロパティーを触れるマテリアルを作ってみる。

(*1)実際はvector2,vector3で良いプロパティーも現状エラーが出るのでvector4にしている。

image.png

EntityのMaterialプロパティーを変更するための設定

1. GraphSettingsAllow material Overrideにチェック

image.png

2. プロパティーを選択した状態で、NodeSettingsのScopeをHybrid Per Instanceに設定する

image.png

MaterialOverrideAssetを作成する

  1. ProjectWindowの中でCreate/Shader/MaterialOverrideAssetからMaterialOverrideAssetを作成する。
  2. MaterialOverrideAssetに作ったMaterialをアタッチュする。

ここでいきなり、AddPropertyOverrideからコードから変更したいプロパティーにチェックを入れてもいいのですが、それだとプロパティー毎に新規.csがアタッチしているMaterialと同じフォルダ内にできて気持ちが悪いので、今回は先にプロパティー用のコードを書いて自動で.csが作成されないようにする。

でも1.csに複数Propertyをまとめたら、それはそれで同じ名前で同じタイプのプロパティーを書いてしまうことになりかねないので、MaterialOverrideProperties的なフォルダの中にプロパティー1個=1.csで管理する方が良い?かもわかめ

コード
using Unity.Entities;
using Unity.Mathematics;
using Unity.Rendering;

namespace ECS_Material_Sample
{
    /// <summary>
    /// TestMaterialのSystem側から変更できるプロパティーをまとめている場所
    /// </summary>
    /// <remarks>
    /// - ↓これを自分で書かなくても、MaterialOverrideAssetでoverrideするプロパティー
    /// - にチェックを入れたら自動で同じコードが作られる。
    /// - けど以下の理由で↓みたいな感じで書いてみている。
    /// - 1. 一個のMaterialのプロパティーを一ファイルにまとめておいたほうが見やすい
    /// - 2. 自動作成だとプロパティー毎に出来た.csがMaterialのあるファイルにできるので気持ち悪い
    /// </remarks>
    
    
    [MaterialProperty("_MainColor")]
    public struct MaterialPropertyMainColor : IComponentData
    {
        public float4 color;
    }
    
    [MaterialProperty("_Scale")]
    public struct MaterialPropertyScale : IComponentData
    {
        public float scale;
    }
    
    [MaterialProperty("_BoxOffset")]
    public struct MaterialPropertyOffset : IComponentData
    {
        /// <summary>
        /// _OffsetはVector2でいいのだけれども...
        /// </summary>
        /// <remarks>
        /// - MaterialOverrideAssetでVector2,Vector3のプロパティーを
        /// - overrideしてみるとわかるのですが、なぜか全部Vector4が表示される。
        /// - そのまま実行するとsizeが違うよ的なエラーが出る。
        /// - [MaterialProperty]アトリビュートの第二引数にsizeがあるので
        /// - 8にするとか試したけどだめだったので、Materialのプロパティー自体はVector2
        /// - だけど、ここではfloat4にしている。Banana
        /// </remarks>
        public float4 offset;
    }
}

MaterialPropertyアトリビュートでプロパティーのReference名を指定する。

もしプロパティーの名前を間違って、存在しないものを書いてしまうとここで.csが作成されてしまう。
そうなった場合は作成された.csとMaterialOverrideAssetを消して、[MaterialProperty()]の名前を直してからMaterialOverrideAssetを作り直す。

Materialを設定しているプレファブにMaterialOverrideコンポーネントを追加して、作ったMaterialOverrideAssetをOverrideAssetにアタッチュ。
image.png

これでMaterialの準備はOK:v::v:

■スクリプト

Authoring

サンプルだと簡易的にAuthoringクラスでCubeプレファブを10000個作る要求Entityを作っている。

コード
using Unity.Entities;
using UnityEngine;

namespace ECS_Material_Sample
{
    public struct SpawnRequestData : IComponentData
    {
        public uint spawnCount;
        public Entity prefabEntity;
    }
    
    public class SpawnPrefabAuthoring : MonoBehaviour
    {
        [SerializeField] private GameObject prefab = null;
        [SerializeField] private uint spawnCount = 100;
        
        private class Baker : Baker<SpawnPrefabAuthoring>
        {
            public override void Bake(SpawnPrefabAuthoring authoring)
            {
                if (authoring.prefab == null || authoring.spawnCount == 0)
                {
                    Debug.LogWarning("プレファブとスポーン数を設定してお金を入れてね");
                    return;
                }
                
                //スポーンリクエストEntity作成
                var requestEntity = GetEntity(TransformUsageFlags.None);

                //リクエスト内容設定
                AddComponent(requestEntity, new SpawnRequestData()
                {
                    prefabEntity = GetEntity(authoring.prefab, TransformUsageFlags.Renderable),
                    spawnCount = authoring.spawnCount
                });
            }
        }
    }
}

SpawnSystem

ここで指定数指定のプレファブのインスタンスを作っている。
同時に作ったインスタンスにMaterialのプロパティーを変更するためのコンポーネントをくっつけている。

コード
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Random = UnityEngine.Random;

namespace ECS_Material_Sample
{
    [BurstCompile]
    public partial class SpawnPrefabSystem : SystemBase
    {
        protected override void OnCreate()
        {
            RequireForUpdate<SpawnRequestData>();
        }

        [BurstCompile]
        protected override void OnUpdate()
        {
            var ecbs = World.GetOrCreateSystemManaged<BeginInitializationEntityCommandBufferSystem>();
            var ecbSpawn = ecbs.CreateCommandBuffer();
            var ecbDestroy = ecbs.CreateCommandBuffer();
            
            foreach (var (requestData, requestEntity) in
                     SystemAPI.Query<SpawnRequestData>().WithEntityAccess())
            {
                float spacing = 1.5f;
                int edgeCount = (int)math.ceil(math.pow(requestData.spawnCount, 1.0f/3.0f));
                
                for (int i = 0; i < requestData.spawnCount; i++)
                {
                    //プレファブのInstance作成
                    var instanceEntity = EntityManager.Instantiate(requestData.prefabEntity);

                    #region 位置を設定

                    // 3次元のインデックスを計算
                    int x = i % edgeCount;
                    int y = (i / edgeCount) % edgeCount;
                    int z = i / (edgeCount * edgeCount);

                    //transform設定
                    ecbSpawn.SetComponent(instanceEntity, new LocalTransform()
                    {
                        Position = new float3(
                            x * spacing,  // X座標
                            y * spacing,  // Y座標
                            z * spacing   // Z座標
                        ),
                        Rotation = quaternion.identity,
                        Scale = 1f 
                    });

                    #endregion

                    #region _MainColor用コンポーネント設定

                    //_MainColorプロパティーをいじるためのコンポーネントを追加
                    ecbSpawn.AddComponent(instanceEntity, new MaterialPropertyMainColor()
                    {
                        //Systemで変更しているので初期値は0
                        color = float4.zero
                    });

                    #endregion

                    #region _Offset用コンポーネント設定

                    //_Offsetを更新するためのランダムな方向を設定
                    var dir = Random.insideUnitCircle;
                    ecbSpawn.AddComponent(instanceEntity, new MaterialPropertyOffset()
                    {
                        offset = new float4(dir.x, dir.y, 0, 0)
                    });

                    #endregion

                    #region _Scale用コンポーネント設定

                    //_Scaleをここで初期化するのみ
                    ecbSpawn.AddComponent(instanceEntity, new MaterialPropertyScale()
                    {
                        scale = Random.Range(1,7)
                    });

                    #endregion
                }

                //リクエスト削除
                ecbDestroy.DestroyEntity(requestEntity);
            }
        }
    }
}

Materialの色プロパティーを更新するSystem

今までのやり方だとMaterial matでmat.SetColorとかSetFloatとかでやっていたのを、MaterialPropertyMainColorをSetCoponentしている箇所でやっている。

コード
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;

namespace ECS_Material_Sample
{
    [BurstCompile]
    public partial class MaterialPropertyMainColorUpdateSystem : SystemBase
    {
        protected override void OnCreate()
        {
            RequireForUpdate<MaterialPropertyMainColor>();
        }

        [BurstCompile]
        protected override void OnUpdate()
        {
            var world = World.DefaultGameObjectInjectionWorld;
            var ecbs = world.GetOrCreateSystemManaged<EndSimulationEntityCommandBufferSystem>();
            var ecb = ecbs.CreateCommandBuffer();

            #region 色計算

            var time = (float)world.Time.ElapsedTime;
            const float speed = 0.1f;
            const float delay = 0.025f;
            
            float3 HsvToRgb(float h, float s, float v)
            {
                float3 rgb = math.abs(math.fmod(h * 6.0f + new float3(0.0f, 4.0f, 2.0f), 6.0f) - 3.0f) - 1.0f;
                rgb = math.saturate(rgb);
                return v * math.lerp(new float3(1.0f), rgb, s);
            }

            #endregion
            
            foreach (var (mainColorComponent, transform, entity) in 
                     SystemAPI.Query<RefRO<MaterialPropertyMainColor>,RefRO<LocalTransform>>().WithEntityAccess())
            {
                #region 色計算

                float hue = math.fmod(time * speed + transform.ValueRO.Position.y * delay, 1.0f);
                float saturation = 0.75f;
                float value = 0.75f;
                float3 rgb = HsvToRgb(hue, saturation, value);

                #endregion
                
                //ここでMaterialの色を変える要求を出している
                ecb.SetComponent(entity, new MaterialPropertyMainColor()
                {
                    color = new float4(rgb.x, rgb.y , rgb.z, 1f)
                });
            }
        }
    }
}

▲Entity関連のScriptが自作のAssemblyDefinitionに属している場合の参照設定について

大体追加する人たち

  • Unity.Entities
  • Unity.Burst
  • Unity.Collections

今回みたいに[MaterialProperty("_Color")]とかするなら↓

  • Unity.Entities.Graphics

物理挙動を使うならUnity Phisicsパッケージを入れて↓とか

  • Unity.Physics

■詰みポ

Entities Graphics辺りのパッケージのバージョンでエラーが出る

:umbrella: - Entities GraphicsをInstallしたはずなのに[Material Property("")]なんぞねえっていうエラーが出るし、Packages/Entities Graphicsの中身を見るとフォルダの中身が消えている。
:sunny: - その場合は一旦Entities、Entities GraphicsをパッケージマネージャーからRemoveして、Entities Graphicsを再度Install。

loading entity scene failed...というエラーが出る

:umbrella: - loading entity scene failed...というエラーが出て結果が見えない。

:sunny: - 一度ProjectウィンドウからSubSceneをダブルクリックで開いた後、元のシーンを開き直してもっかい実行したら”行けたりする”。

■猫の腹を撫でる

neko.gif

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?