[Unity] Dungeon Gunner (4) - Dungeon Creation Design [2]

suhan0304·2024년 1월 12일
0
post-thumbnail

스크립터블 오브젝트

스크립터블 개체 클래스를 사용하여 룸 노드 데이터를 저장한다. 룸 노드 형식을 저장하기 위해 스크립터블 클래스를 생성하고 이러한 룸 노드 형식의 목록을 저장할 클래스도 생성한다.

또한 생성한 스크립터블 개체와 같이 직렬화된 데이터가 올바르게 채워졌는지 확인하는 유효성 검사 또한 수행할 수 있도록 한다.

데이터를 저장하기 위한 스크립터블 클래스의 개체 Asset을 추가할수록 소실된 데이터를 개발자에게 알려주는 유효성 검사가 중요해진다. 이러한 유효성 검사가 없으면 오류가 날 확률이 높다.

룸 노드 데이터 구조

룸 노드 그래프의 데이터 구조가 어떤 형식으로 구성될 것인지를 확인해보자. 일단 먼저 RoomNodeGraphSO로 완전한 노드 그래프를 나타낸다. 이 룸 노드 그래프는 각 개별 방 노드를 나타내는 방 노드 스크립터블 오브젝트로 구성된다. 이때 각기 방 노드 즉, RoomNodeSO는 RoomNodeTypeSO로 방 유형을 정의한다.

SOScriptable Object의 약자이다.

룸 노드 계층

룸 노드가 어떤 형식으로 계층 구조로 표현되는지 확인해보자. 각 노드 별로 부모 노드 ID 목록과 자녀 노드 ID 목록이 있다. 이를 통해 노드 그래프에서 노드의 레이아웃을 나타낼 수 있다. 이를 통해 1번 RoomNodeSO가 2, 3번 RoomNodeSo와 링크되어있고 그 아래의 자식 룸 노드들로 링크되는것을 확인할 수 있다.

이제 코드로 위 내용들을 구현해보자!


RoomNodeGraphSO.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


[CreateAssetMenu(fileName = "RoomNodeGraph", menuName = "Scriptable Objects/Dungeon/Room Node Graph")]
public class RoomNodeGraphSO : ScriptableObject
{
    [HideInInspector] public RoomNodeTypeListSO roomNodeTypeList;
    [HideInInspector] public List<RoomNodeGraphSO> roomNodeList = new List<RoomNodeSO>();
    [HideInInspector] public Dictionary<string, RoomNodeGraphSO> roomNodeDictionary = new Dictionary<string, RoomNodeSO>();
}

RoomNodeSO.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RoomNodeSO : ScriptableObject
{
    [HideInInspector] public string id;
    [HideInInspector] public List<string> parentRoomNodeIDList = new List<string>();
    [HideInInspector] public List<string> childRoomNodeIDList = new List<string>();
    [HideInInspector] public RoomNodeGraphSO roomNodeGraph;
    public RoomNodeTypeSO roomNodeType;
    [HideInInspector] public RoomNodeTypeListSO roomNodeTypeList;
}

RoomNodeTypeSO.cs

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

[CreateAssetMenu(fileName = "RoomNodeType", menuName = "Scriptable Objects/Dungeon/Room Node Type")]
public class RoomNodeTypeSO : ScriptableObject
{
    public string roomNodeTypeName;

    #region Header
    [Header("Only flag the RoomNodeTypes that should be visible in the editor")]
    #endregion Header
    public bool displayInNodeGraphEditor = true;
    #region Header
    [Header("One Type Should Be A Corridor")]
    #endregion Header
    public bool isCorridor = true;
    #region Header
    [Header("One Type Should Be A CorridorNS")]
    #endregion Header
    public bool isCorridorNS = true;
    #region Header
    [Header("One Type Should Be A CorridorEW")]
    #endregion Header
    public bool isCorridorEW = true;
    #region Header
    [Header("One Type Should Be A Entrance")]
    #endregion Header
    public bool isEntrance = true;
    #region Header
    [Header("One Type Should Be A Boss Room")]
    #endregion Header
    public bool isBossRoom = true;
    #region Header
    [Header("One Type Should Be None (Unassigned)")]
    #endregion Header
    public bool isNone = true;
    
    #region Validation
#if UNITY_EDITOR
    private void OnValidate()
    {
        HelperUtilities.ValidateCheckEmptyString(this, nameof(roomNodeTypeName), roomNodeTypeName); 
    }
#endif
    #endregion
}

HelperUtilities는 아래 HelperUtilities.cs를 참고한다.

#if UNITY_EDITOR ~ #endif 사이의 내용은 유니티 에디터 환경에서만 작동한다. 즉, 실제 게임 플레이 상에서는 실행되지않는 코드이고 에디터, 즉 개발 환경에서만 유효성 검사가 실행되는 것이다.

RoomNodeTypeListSO.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "RoomNodeTypeListSO", menuName = "Scriptable Objects/Dungeon/Room Node Type List")]
public class RoomNodeTypeListSO : ScriptableObject
{
    #region Header ROOM NODE TYPE LIST
    [Space(10)]
    [Header("ROOM NODE TYPE LIST")]
    #endregion
    #region Tooltip
    [Tooltip("This list should be populated with all the RoomNodeTypeSO for the game - it is used instead of an enum")]
    #endregion
    public List<RoomNodeTypeSO> list;

    #region Validation
#if UNITY_EDITOR
    private void OnValidate()
    {
        HelperUtilities.ValidateCheckEnumerableValues(this, nameof(list), list);
    }
#endif
    #endregion
}

아래는 유효성 검사를 도와주는 HelperUtilities 스크립트이다.

HelperUtilities.cs

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class HelperUtilities
{
    /// <summary>
    /// Empty String Debug check 
    /// 문자열(stringToCheck)이 공백인지 확인하는 유효성 검사
    /// </summary>
    /// <param name="thisObject"></param>
    /// <param name="fieldName"></param>
    /// <param name="stringToCheck"></param>
    /// <returns></returns>
    public static bool ValidateCheckEmptyString(Object thisObject, string fieldName, string stringToCheck)
    {
        if(stringToCheck == "")
        {
            Debug.Log(fieldName + " is empty and must contain a value in object " + thisObject.name.ToString());
            return true;
        }
        return false;
    }

    /// <summary>
    /// list empty or contains null value check - returns true if there is an error
    /// 리스트가 비어있거나 null value가 있는지 확인하는 유효성 검사
    /// </summary>
    /// <param name="thisObject"></param>
    /// <param name="fieldName"></param>
    /// <param name="enumerableObjectToCheck"></param>
    /// <returns></returns>
    public static bool ValidateCheckEnumerableValues(Object thisObject, string fieldName, IEnumerable enumerableObjectToCheck)
    {
        bool error = false;
        int count = 0;

        foreach(var item in enumerableObjectToCheck)
        {
            if (item == null)
            {
                Debug.Log(fieldName + " has null values in object " + thisObject.name.ToString());
                error = true;
            }
            else
            {
                count++;
            }
        }

        if(count == 0)
        {
            Debug.Log(fieldName + " has no values in object " + thisObject.name.ToString());
            error = true;
        }

        return error;   
    }
}

위와 같이 스크립트를 작성해서 기본적인 스크립터블 오브젝트 구조를 모두 구현해낼 수 있다. 이제 이러한 스크립터블 오브젝트를 이용해서 룸 노드 그래프를 생성할 수 있는 에디터를 완성 시키고 에디터를 이용해 만든 룸 노드 그래프를 기반으로 던전의 룸 노드들이 배치되도록 한다.


profile
Be Honest, Be Harder, Be Stronger

0개의 댓글

관련 채용 정보