Unity DOTS/따라하며 배우기

UNITY DOTS - State Machine(상태 머신) 만들기 #3 Guard 이동

개양반 2022. 8. 6.
728x90

오늘 알아볼 내용

두 개의 Waypoints를 설치합니다. Guard가 Entity로 전환될 때 Waypoint 1개를 목적지로 설정하고 해당 목적지로 이동하는 것에 대해 다룹니다.


GuardAIUtility

Guard 의 여러 행동들을 설정할 때 사용할 예정인 GuardAIUtility.cs 파일 생성하고 아래의 코드를 작성합니다.

public class GuardAIUtility
{
    public const float kStopDistanceSq = 0.4f;
}

#코드 설명

목적지에 도달했다고 판단하는 거리입니다. 목적지와 Guard의 현재위치의 거리가 kStopDistanceSq보다 작아지면 도착했다고 판단합니다.

 


Data

Waypoints 와 현재 이동해야 하는 위치를 저장해야 합니다. 아래의 코드를 GuardAuthoring.cs에 추가합니다.

using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
using System.Collections.Generic;

[InternalBufferCapacity(8)]
public struct WaypointPosition : IBufferElementData
{
    public float3 Value;
}

public struct TargetPosition : IComponentData
{
    public float3 Value;
}

#코드설명

IBufferElementData는 데이터의 크기가 가변적으로 변할 수 있는 동적 버퍼입니다. InternalBufferCapacity(8) 는 동적버퍼의 크기를 지정한 겁니다. 최대 8개의 Waypoin를 참조할 수 있으며 이보다 커질 경우 청크 외부에 큰 배열을 만들어서 데이터를 그 쪽으로 옮기므로 성능이 안 좋아집니다. InternalBufferCapacity(8) 는 기획에 따라 크기를 잘 지정해줘야 합니다. IBufferElementData에 대한 메뉴얼은 아래의 링크를 참고합니다.

[Unity DOTS/Dots Custom Manual] - UNITY DOTS - Dynamic Buffer Component 에 대해 알아보자.

TargetPosition 는 Guard가 이동해야 하는 위치를 저장하는데 사용됩니다.


System

목적지로 이동하는 System을 구현합니다. MoveTowardTargetSystem.cs에 아래의 코드를 작성합니다.

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;

public partial class MoveTowardTargetSystem : SystemBase
{
    // 최신 버전부터 OnUpdate에 BurstCompile 가능
    [BurstCompile]
    protected override void OnUpdate()
    {
        var deltaTime = Time.DeltaTime;

        Entities
            .WithName("MoveTowardTarget")
            .ForEach((

                ref Translation guardPosition,                // Guard의 현재 위치 
                ref Rotation guardRotation,                   // Guard가 바라보는 방향
                in TargetPosition targetPosition,             // 이동해야 할 위치 (Guard에만 존재하는 Componet)
                in MovementSpeed movementSpeed) => {          // 이동 속도

                    // 남은 거리
                    var vectorToTarget = targetPosition.Value - guardPosition.Value;

                    // 도착 여부 확인 
                    if (math.lengthsq(vectorToTarget) > GuardAIUtility.kStopDistanceSq)
                    {

                        var moveDirection = math.normalize(vectorToTarget);

                        // 위치, 방향 변경
                        guardRotation.Value = quaternion.LookRotation(new float3(moveDirection.x, 0.0f, moveDirection.z), math.up());
                        guardPosition.Value = guardPosition.Value + moveDirection * movementSpeed.MetersPerSecond * deltaTime;
                    }

                    // 멀티스레드에서 병렬로 처리
                }).ScheduleParallel();
    }
}

 


Authoring

Guard는 게임오브젝트로 존재하다가 Runtime이 되면 Entity로 변환시킬 겁니다. GuardEntity에 필요한 데이터를 추가하는 코드를 GuardAuthoring.cs에 아래와 같이 작성합니다.

[DisallowMultipleComponent]
public class GuardAuthoring : MonoBehaviour, IConvertGameObjectToEntity
{
    public Transform[] Waypoints;
    public float MovementSpeedMetersPerSecond = 3.0f;


    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddComponents(entity, new ComponentTypes(
            new ComponentType[]
            {
                typeof(TargetPosition),
                typeof(MovementSpeed),
                typeof(WaypointPosition)
            }));

		// Buffer 얻어오기
        var buffer = dstManager.GetBuffer<WaypointPosition>(entity);
        
        // Buffer에 값 채우기
        foreach (var waypointTransform in Waypoints)
        {
            buffer.Add(new WaypointPosition { Value = waypointTransform.position });
        }
        dstManager.SetComponentData(entity, new TargetPosition { Value = buffer[0].Value });
        dstManager.SetComponentData(entity, new MovementSpeed { MetersPerSecond = MovementSpeedMetersPerSecond });

    }
}

#코드 설명

IComponentData와 다르게 IBufferElementData는 dstManager.GetBuffer<WaypointPosition>(entity); 식으로 Entity의 Buffer Data를 가져옵니다.

 


GameObject

1. WayPoint 만들기

Hierarchy 뷰에서 빈게임오브젝트 2개를 만들고 이름을 Waypoint1, Waypoint2로 변경합니다. 

 

 

2. Guard 만들기

Hierarchy 뷰에서 Capsule 2개를 만들고 부모, 자식 관계로 만듭니다. 하나는 Guard가 현재 바라보는 방향을 알기 위해 Nose로 만들겁니다. 아래의 이미지처럼 설정합니다.

 

유니티 재생 버튼을 눌러 테스트합니다.

댓글

💲 추천 글