BehaviorTree / Runtime / Blackboard / Blackboard.cs
Blackboard.cs
Raw
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace AI.BT
{
    [CreateAssetMenu(fileName = "Blackboard", menuName = "AI/Blackboard", order = 1)]
    public class Blackboard : ScriptableObject, ISerializationCallbackReceiver
    {
        public Dictionary<string, BlackboardKey> Keys { get; private set; } = new Dictionary<string, BlackboardKey>();

        [SerializeField] private string[] _Keys;

        [SerializeField] private BlackboardKey[] _Values;

        public Action<string> OnKeyAdded;

        public Action<string> OnKeyRemoved;

        public Action<string> OnKeyChanged;

        private List<Action> Observers = new List<Action>();

        public void Add(string name)
        {
            if (Keys.ContainsKey(name))
            {
                Debug.LogError($"Key name - {name} already exists in blackboard!");
            }

            Keys.Add(name, new BlackboardKey());
            _Keys = Keys.Keys.ToArray();
            _Values = Keys.Values.ToArray();

            OnKeyAdded?.Invoke(name);
        }

        public void Remove(string name) 
        {
            if (!Keys.ContainsKey(name)) return;

            Keys.Remove(name);
            _Keys = Keys.Keys.ToArray();
            _Values = Keys.Values.ToArray();

            OnKeyRemoved?.Invoke(name);
        }

        public bool Contains(string name)
        {
            return Keys.ContainsKey(name);
        }

        //try get key if no key is found returns the default object
        public bool TryGet(string name, out object value)
        {
            if (Keys.ContainsKey(name))
            {
                value = Keys[name].Get().Value;
                return true;
            }

            value = default(object);
            return false;
        }

        //try get key if no key is found returns the default object
        public bool TryGet<T>(string name, out T value)
        {
            object _value = null;
            if (TryGet(name, out _value))
            {
                if(_value != null & _value is T)
                {
                    value = (T)_value;
                    return true;
                }
            }

            value = default(T);
            return false;
        }

        //try set key returns if key was set
        public bool TrySet<T>(string name, T value) 
        {
            if(Keys.ContainsKey(name))
            {
                Keys[name].Get().Value = value;

                NotifyObservers();

                return true;
            }

            return false;
        }

        public bool IsSet(string name)
        {
            return Keys.ContainsKey(name) && Keys[name].Get().Value != null;
        }

        public bool IsUnSet(string name)
        {
            return Keys.ContainsKey(name) && Keys[name].Get().Value == null;
        }

        public bool Rename(string oldname, string newname)
        {
            if(Keys.ContainsKey(oldname) && !Keys.ContainsKey(newname))
            {
                Keys.Remove(oldname);
                Keys.Add(newname, null);
                OnKeyChanged?.Invoke(newname);
                return true;
            }

            return false;
        }

        public void Clear() { Keys.Clear(); }

        public Blackboard Clone()
        {
            var blackboard = Instantiate(this);
            blackboard.Keys = Keys;

            return blackboard;
        }

        public void OnBeforeSerialize()
        {
            
        }

        public void OnAfterDeserialize()
        {
            Keys.Clear();
         
            if(_Keys != null && _Values != null && _Keys.Length == _Values.Length) 
            {
                for(int i = 0; i < _Keys.Length; i++ )
                {
                    if (!Keys.ContainsKey(_Keys[i]))
                        Keys.Add(_Keys[i], _Values[i]);
                }
            }
        }

        internal void AddObserver(Action onBlackboardKeyChanged)
        {
            Observers.Add(onBlackboardKeyChanged);
        }

        internal void RemoveObserver(Action onBlackboardKeyChanged)
        {
            Observers.Remove(onBlackboardKeyChanged);
        }

        private void NotifyObservers()
        {
            foreach (var observer in Observers)
            {
                observer.Invoke();
            }
        }
    }
}