Unity/개발연습

[Unity] Behaviour Tree Editor 제작기 - 6

민트초밥 2024. 9. 4. 16:24

# Node 초기화 함수 추가

 

런타임 중에 Awake나 Start 함수에서 Node를 초기화하는 기능이 필요할 것 같아서 추가했다.

Root Node부터 시작해서 하위 Node들의 Init 함수를 재귀형태로 실행한다.

public void Init(BehaviourTreeContext context)
{
    this.Context = context;

    if (string.IsNullOrEmpty(RootNodeGuid))
        Debug.LogError($"{nameof(BehaviourTree)} : Root Node Guid is Empty");

    rootNode = FindNode(RootNodeGuid);

    InitRecursive(rootNode);
}


private void InitRecursive(BehaviourNode node)
{
    node.Init(this);

    List<string> nodeGuidList = FindNode(node.Guid).ChildNodeGuidList;

    foreach(string guid in nodeGuidList)
        InitRecursive(FindNode(guid));
}
public class BehaviourTreeController : MonoBehaviour
{
    public BehaviourTree BehaviourTree;

    private void Awake()
    {
        BehaviourTreeContext context = new BehaviourTreeContext(gameObject);
        BehaviourTree.Init(context);
    }

    private void Update()
    {
        BehaviourTree.Evaluate();
    }
}

 

 

 


 

# Blackboard

 

Behaviour Tree에서 Node끼리의 데이터를 공유할 때 Blackboard란 걸 만들어서 사용하는 것 같다.

 

 

Behavior Tree (4) - 데이터 전달하기(Blackboard)

이전 글: 2022.08.30 - [Game AI] - Behavior Tree (3) - Parallel Behavior Tree (3) - Parallel 이전 글 : 2022.08.27 - [Game AI] - Behavior Tree (2) - 확률, Decorator, 리소스 보호 Behavior Tree (2) - 확률, Decorator, 리소스 보호 이전 글

tsyang.tistory.com

 

Unreal 공식 문서

 

 

 

 

예를 들어, 게임에서 적 AI가 플레이어를 쫓아가는 행동 트리를 구성한다고 할 때, Blackboard에는 아래와 같은 데이터가 저장되고 사용될 수 있다.

  • 플레이어의 현재 위치
  • 적 AI의 현재 체력
  • 적 AI가 현재 따라가고 있는 경로

 

 

 

 

데이터 타입을 한정하기 위해서 Dictionary 안에 Dictionary가 들어가는 형태로 만들어서 Type의 따라 새로운 Dictionary를 만들어서 데이터를 관리한다.

public class BehaviourTreeBlackboard
{
    private Dictionary<Type, IDictionary> blackboardDictionary = new Dictionary<Type, IDictionary>();


    public void SetData<T>(string keyName, T value)
    {
        if (!blackboardDictionary.ContainsKey(typeof(T)))
            blackboardDictionary.Add(typeof(T), new Dictionary<string, T>());

        IDictionary dic = blackboardDictionary[typeof(T)];

        if (dic.Contains(keyName))
        {
            dic[keyName] = value;
        }
        else
        {
            dic.Add(keyName, value);
        }
    }


    public T GetData<T>(string keyName)
    {
        if (!blackboardDictionary.ContainsKey(typeof(T)))
            return default(T);

        IDictionary dic = blackboardDictionary[typeof(T)];

        if (!dic.Contains(keyName))
            return default(T);

        return (T)dic[keyName];
    }
}

 

public override void Init(BehaviourTree tree)
{
    base.Init(tree);

    tree.Blackboard.SetData<string>("Test Key", "Test String Value");
}

public override NodeState Evaluate()
{
    string testString = tree.Blackboard.GetData<string>("Test Key");
    Debug.LogWarning("Blackboard 출력 : " + testString);

    return NodeState.Success;
}

 

 


 

# Behaviour Tree Settings

 

Node별로 이미지를 추가해서 노드가 어떤 기능인지 나타내는 기능을 만들었는데, 개별 Tree마다 설정할게 아니라 모든 Tree가 공유하는 이미지나 값들을 추가하는 게 더 나을 것 같아서 만들게 되었다.

 

 

 

 

 

Setting 값들을 저장하고 관리하는 ScriptableObject

public class BehaviourTreeSettings : ScriptableObject
{
    public const string SettingsPath = "Assets/BehaviourTreeEditor/BehaviourTreeSettings.asset";

    public Texture2D SequenceTexture;

    private static BehaviourTreeSettings instance;

    public static BehaviourTreeSettings Instance
    {
        get
        {
            if (instance == null)
            {
                instance = GetOrCreateSettings();
            }

            return instance;
        }
    }

    public static BehaviourTreeSettings GetOrCreateSettings()
    {
        var settings = AssetDatabase.LoadAssetAtPath<BehaviourTreeSettings>(SettingsPath);

        if (settings == null)
        {
            settings = ScriptableObject.CreateInstance<BehaviourTreeSettings>();
            AssetDatabase.CreateAsset(settings, SettingsPath);
            AssetDatabase.SaveAssets();
        }

        return settings;
    }

    public void Save()
    {
        EditorUtility.SetDirty(this);
        AssetDatabase.SaveAssets();
    }
}

 

 

 

 

ScriptableObject에 설정된 Setting 값들을 [ProjectSettings] 창에서 보여주기 위한 SettingsProvider 클래스

public class BehaviourTreeSettingsProvider : SettingsProvider
{
    private static BehaviourTreeSettings settings;

    public BehaviourTreeSettingsProvider(string path, SettingsScope scopes, IEnumerable<string> keywords = null) : base(path, scopes, keywords)
    {
        settings = BehaviourTreeSettings.GetOrCreateSettings();
    }

    public override void OnGUI(string searchContext)
    {
        base.OnGUI(searchContext);

        GUILayout.Label("Node Texture", EditorStyles.boldLabel);

        settings.SequenceTexture = EditorGUILayout.ObjectField("Sequence Node Texture", settings.SequenceTexture, typeof(Texture2D), false) as Texture2D;

        if (GUI.changed)
        {
            settings.Save();
        }
    }

    [SettingsProvider]
    public static SettingsProvider CreateBehaviourTreeSettingsProvider()
    {
        return new BehaviourTreeSettingsProvider("Project/Behaviour Tree Settings", SettingsScope.Project);
    }
}

 

 

 

 

 

 

 

 

GitHub - mintchobab/Unity_Practice_Editor

Contribute to mintchobab/Unity_Practice_Editor development by creating an account on GitHub.

github.com

 

반응형