土鍋で雑多煮

UnityでXR・ゲーム開発をしています。学んだことや備忘録、趣味の記録などを書いていきます。

MENU

UnityECSで都市開発シミュレーションゲームを作る【その2】~店を作る~

はじめに

どうも、土鍋です。

前回に引き続き、都市開発シミュレーションゲームをUnityECSで実装していこうと思います。

前回も書きましたが、現状手探りで記事を書いているので、より良い実装ができ次第、記事も更新していきます。

前回の記事はこちら

donabenabe.hatenablog.com

店を作る

今回は住民が訪れる店などの建物を作って、住民がその建物に移動するようにしてみます。

建物のEntityを作る

コードの書き方に関しては前回同様なので説明は省略します。

Component

using Unity.Entities;
using UnityEngine;

namespace Building
{
    [System.Serializable]
    public struct BuildingBase : IComponentData
    {
        public BuildingType buildingType; // 建物のタイプ
        public int numberEmployee; // 従業員数
    }
}

Baker

using Unity.Entities;
using UnityEngine;

namespace Building
{
    public class BuildingAuthoring : MonoBehaviour
    {
        [SerializeField]
        private BuildingType _buildingType;
        [SerializeField]
        private int _numberEmployee;
        
        class Baker : Baker<BuildingAuthoring>
        {
            public override void Bake(BuildingAuthoring src)
            {
                var data = new BuildingBase()
                {
                    buildingType = src._buildingType,
                    numberEmployee = src._numberEmployee
                };
                AddComponent(GetEntity(TransformUsageFlags.Dynamic),data);
            } 
        }
    }
}

システムは後日追加予定。

住民が目的地を設定して訪問するように

前回作った住民のSystemに一番近い建物に向かうコードを追加してみます。

using Building;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;

namespace Citizen
{
    public partial struct CitizenSystem : ISystem
    {
        private EntityQuery _buildingQuery;
        
        [BurstCompile]
        public void OnCreate(ref SystemState state)
        {
            _buildingQuery = state.GetEntityQuery(typeof(LocalTransform), typeof(BuildingBase));
        }
        
        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            // Jobの発行処理
            var job = new CitizenUpdateJob()
            {
                Elapsed = (float)SystemAPI.Time.ElapsedTime,
                buildingsTransforms = _buildingQuery.ToComponentDataArray<LocalTransform>(Allocator.TempJob)
            }; // 初期化
            job.ScheduleParallel(); // Jobの予約
        }
    }
    
    [BurstCompile]
    partial struct CitizenUpdateJob : IJobEntity
    {
        public float Elapsed; // 経過時間
        public NativeArray<LocalTransform> buildingsTransforms; // 全建物
        private bool _isNowDestination;

        void Execute(ref CitizenBase citizen, ref LocalTransform transform)
        {
            LocalTransform closestBuildingTransform = default;
            float minDistanceSq = float.MaxValue;

            // 各建物の位置と住民の位置を比較して最も近いものを探す
            foreach (var buildingTransform in buildingsTransforms)
            {
                float distanceSq = math.distancesq(buildingTransform.Position, transform.Position);

                if (distanceSq < minDistanceSq)
                {
                    minDistanceSq = distanceSq;
                    closestBuildingTransform = buildingTransform;
                }
            }

            citizen.destination = closestBuildingTransform.Position;
            
            Move(citizen, ref transform);
            
        }

        void Move(CitizenBase citizen, ref LocalTransform transform)
        {
            // 現在位置から目的地に向かって一定のスピードで移動
            transform.Position = Vector3.MoveTowards(transform.Position, citizen.destination, citizen.moveSpeed * Elapsed);
        
            // 目的地に到達したかどうかのチェック
            if (Vector3.Distance(transform.Position, citizen.destination) < 0.01f)
            {
                Debug.Log("目的地に到達しました!");
                _isNowDestination = true;
            }
            else
            {
                _isNowDestination = false;
            }
        }
    }
}

コードの解説

private EntityQuery _buildingQuery;
        
[BurstCompile]
public void OnCreate(ref SystemState state)
{
    _buildingQuery = state.GetEntityQuery(typeof(LocalTransform), typeof(BuildingBase));
}

OnCreateメソッドはいわゆるUnity従来のStartにあたるメソッドでEntity生成時に実行されるイメージです。
この部分でBuildingBaseを持つEntityのQueryを取得しています。

buildingsTransforms = _buildingQuery.ToComponentDataArray<LocalTransform>(Allocator.TempJob)

CitizenUpdateJobのNativeArrayにクエリからのLocalTransformデータを代入してあげます。
こうすることでCitizenUpdateJob内で住民からもっとも近い建物を取得して移動するようにできます。

正直、実装として微妙な気はしているので、後日書き直します。

参考文献

www.f-sp.com

docs.unity3d.com

docs.unity3d.com

qiita.com