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(); } } } }