diff --git a/Docs/dsl.md b/Docs/dsl.md new file mode 100644 index 0000000..bc0a79f --- /dev/null +++ b/Docs/dsl.md @@ -0,0 +1,111 @@ +# AkiBT DSL + +AkBT.DSL is a domain-specific language designed for AkiBT, use standard AST (Abstract Syntax Tree) to build behavior tree directly instead of generating json as intermediate. + +Inspired by Game AI Pro and .Net's C# version for LLVM's official tutorial of Kaleidoscope. + +## What is DSL + +A Domain-Specific Language (DSL) is a computer language that's targeted to a particular kind of problem, rather than a general purpose language that's aimed at any kind of software problem. + +[See Reference Article](https://martinfowler.com/dsl.html) + +## How to use + +``` +/// +It's a comment +The following is a behavior tree written using AkiBTDSL +/// +Vector3 destination (0,0,0) +Vector3 myPos (0,0,0) +Float distance 1 +Vector3 subtract (0,0,0) +Parallel(children:[ + Sequence(children:[ + Vector3Random(xRange:(-10,10),yRange:(0,0),zRange:(-10,10),operation:1, + storeResult=>destination ), + DebugLog(logText:"This is a log: agent get a new destination."), + TimeWait(waitTime:10) + ]), + Sequence(children:[ + Sequence(children:[ + TransformGetPosition(storeResult=>myPos), + Vector3Operator(operation:1,firstVector3=>myPos, + secondVector3=>destination,storeResult=>subtract), + Vector3GetSqrMagnitude(vector3=>subtract,result=>distance) + ]), + Selector(abortOnConditionChanged: false, children:[ + FloatComparison(evaluateOnRunning:false,float1=>distance, + float2:4,operation:5,child: + Sequence(abortOnConditionChanged:false,children:[ + NavmeshStopAgent(isStopped:false), + NavmeshSetDestination(destination=>destination) + ]) + ), + NavmeshStopAgent(isStopped:true) + ]) + ]) +]) + + +``` + +The above behavior tree is the patrol AI behavior tree in AkiBT Example, it will get a new position every 10 seconds and move to it, if the distance from the target point is less than 2, it will stop + +The main body of DSL can be divided into two parts, public variables and nodes. The declaration of public variables needs to specify the type, name and value. + +If the type is wrapped with `$`, global variables will be bound at runtime, for example: + +``` +$Vector3$ TargetPosition (0,0,0) +``` + +If the variable type is Object (SharedObject), you can declare the type name before declaring the value. For example: +``` +Object navAgent "UnityEngine.AIModule,UnityEngine.AI.NavMeshAgent" Null +``` + +The type name's format is `{Assembly Name},{NameSpace}.{ClassName}` or use `Type.AssemblyQualifiedName`. + +Then the variable is set to global and has the ``NavMeshAgent`` type restriction. + +For nodes, we will skip the Root node (because all behavior trees enter from the Root), and start writing directly from the Root's child nodes. + + +For a node, you need to declare its type. + + +For ordinary variables that do not use the default value of the node, you need to declare its name (or use AkiLabelAttribute to alter field's name) and add ':' to assign + + +For the shared variable in the node, if you don’t need to refer to the shared variable of the public variable, you can assign it directly, for example + +``` +TimeWait(waitTime:10) +``` + +For shared variables that need to be referenced, use the '=>' symbol plus the name of the public variable that needs to be referenced, for example +``` +NavmeshSetDestination(destination=>myDestination) +``` + +## Compatible with AkiLabel + +DSL supports use `AkiLabelAttribute`'s value as name to build behavior tree. For example: + +``` +Vector3 玩家位置 (0,0,0) +序列 (子节点:[ + 获取玩家位置 (位置=>玩家位置), + 移动至玩家(目标=>玩家位置) +]) +``` + +Which is quite cool! + +## Reference + +https://llvm.org/docs/tutorial/MyFirstLanguageFrontend/index.html + +https://github.com/dotnet/LLVMSharp/blob/main/samples/KaleidoscopeTutorial \ No newline at end of file diff --git a/Docs/dsl.md.meta b/Docs/dsl.md.meta new file mode 100644 index 0000000..7485845 --- /dev/null +++ b/Docs/dsl.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d420b3cff0a752f4e9ed07a11ecca3a9 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Core/Editor/APIUpdateConfigEditor.cs b/Editor/Core/Editor/APIUpdateConfigEditor.cs new file mode 100644 index 0000000..5e9c886 --- /dev/null +++ b/Editor/Core/Editor/APIUpdateConfigEditor.cs @@ -0,0 +1,41 @@ +using System; +using UnityEditor; +using UnityEditor.Experimental.GraphView; +using UnityEngine; +namespace Kurisu.AkiBT.Editor +{ + [CustomPropertyDrawer(typeof(APIUpdateConfig.SerializeType))] + public class SerializeTypeDrawer : PropertyDrawer + { + private const string NullType = "Null"; + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(position, label, property); + var reference = property.FindPropertyRelative("nodeType"); + if (EditorGUI.DropdownButton(position, new GUIContent(reference.stringValue), FocusType.Keyboard)) + { + var provider = ScriptableObject.CreateInstance(); + provider.Initialize((type) => + { + var serializeType = new APIUpdateConfig.SerializeType(type); + reference.stringValue = serializeType != null ? serializeType.nodeType : NullType; + property.serializedObject.ApplyModifiedProperties(); + }); + SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)), provider); + } + EditorGUI.EndProperty(); + } + } + [CustomEditor(typeof(APIUpdateConfig))] + public class APIUpdateConfigEditor : UnityEditor.Editor + { + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + if (GUILayout.Button("Update API")) + { + APIUpdater.UpdateAPI(typeof(BehaviorTreeAsset), target as APIUpdateConfig); + } + } + } +} diff --git a/Editor/Core/Utility/APIUpdateConfig.cs.meta b/Editor/Core/Editor/APIUpdateConfigEditor.cs.meta similarity index 100% rename from Editor/Core/Utility/APIUpdateConfig.cs.meta rename to Editor/Core/Editor/APIUpdateConfigEditor.cs.meta diff --git a/Editor/Core/Editor/BehaviorTreeEditor.cs b/Editor/Core/Editor/BehaviorTreeEditor.cs index 169fef9..455aa03 100644 --- a/Editor/Core/Editor/BehaviorTreeEditor.cs +++ b/Editor/Core/Editor/BehaviorTreeEditor.cs @@ -29,6 +29,8 @@ public override VisualElement CreateInspectorGUI() { myInspector.Add(new SharedVariablesFoldout(instance.BlackBoard, () => { + // Not serialize data in playing mode + if (Application.isPlaying) return; tree.SetBehaviorTreeData(instance.GetData()); EditorUtility.SetDirty(target); })); @@ -64,6 +66,8 @@ public override VisualElement CreateInspectorGUI() { myInspector.Add(new SharedVariablesFoldout(instance.BlackBoard, () => { + // Not serialize data in playing mode + if (Application.isPlaying) return; tree.SetBehaviorTreeData(instance.GetData()); EditorUtility.SetDirty(target); })); diff --git a/Editor/Core/Editor/GraphEditorWindow.cs b/Editor/Core/Editor/GraphEditorWindow.cs index 72c1fc5..e264e02 100644 --- a/Editor/Core/Editor/GraphEditorWindow.cs +++ b/Editor/Core/Editor/GraphEditorWindow.cs @@ -80,7 +80,7 @@ private static bool OnOpenAsset(int instanceId, int _) return false; } #pragma warning restore IDE0051 - [MenuItem("Tools/AkiBT/AkiBT Editor")] + [MenuItem("Tools/AkiBT/AkiBT Editor", priority = 0)] private static void ShowEditorWindow() { string path = EditorUtility.SaveFilePanel("Select ScriptableObject save path", Application.dataPath, "BehaviorTreeSO", "asset"); @@ -276,7 +276,6 @@ protected virtual void DrawLeftToolBar() ShowNotification(guiContent); } } - GUI.enabled = true; bool newValue = GUILayout.Toggle(Setting.AutoSave, "Auto Save", EditorStyles.toolbarButton); if (newValue != Setting.AutoSave) { @@ -294,7 +293,6 @@ protected virtual void DrawLeftToolBar() } } - GUI.enabled = !Application.isPlaying; if (GUILayout.Button("Copy from Asset", EditorStyles.toolbarButton)) { string path = EditorUtility.OpenFilePanel("Select asset to copy", Setting.LastPath, "asset"); @@ -319,6 +317,8 @@ protected virtual void DrawRightToolBar() { NodeAutoLayoutHelper.Layout(new BehaviorTreeLayoutConvertor().Init(graphView)); } + + GUI.enabled = !Application.isPlaying; if (GUILayout.Button("Save to Json", EditorStyles.toolbarButton)) { var serializedData = graphView.SerializeTreeToJson(); @@ -329,12 +329,12 @@ protected virtual void DrawRightToolBar() Setting.LastPath = info.Directory.FullName; EditorUtility.SetDirty(setting); File.WriteAllText(path, serializedData); - Debug.Log($"{GraphView.EditorName}:Save to json file succeed!"); + Debug.Log($"{GraphView.EditorName}: Save to json file succeed!"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } } - GUI.enabled = !Application.isPlaying; + if (GUILayout.Button("Copy from Json", EditorStyles.toolbarButton)) { string path = EditorUtility.OpenFilePanel("Select json file to copy", Setting.LastPath, "json"); @@ -362,7 +362,7 @@ protected IBehaviorTreeContainer LoadDataFromFile(string path) } catch { - ShowNotification(new GUIContent($"Invalid Path:{path}, please pick ScriptableObject inherited from BehaviorTreeSO")); + ShowNotification(new GUIContent($"Invalid Path: {path}, please pick ScriptableObject inherited from BehaviorTreeSO")); return null; } } diff --git a/Editor/Core/Editor/ServiceEditorWindow.cs b/Editor/Core/Editor/ServiceEditorWindow.cs index 3b87654..691caef 100644 --- a/Editor/Core/Editor/ServiceEditorWindow.cs +++ b/Editor/Core/Editor/ServiceEditorWindow.cs @@ -10,7 +10,7 @@ namespace Kurisu.AkiBT.Editor { public class ServiceEditorWindow : EditorWindow { - [MenuItem("Tools/AkiBT/AkiBT Service")] + [MenuItem("Tools/AkiBT/AkiBT Service", priority = 1)] private static void ShowEditorWindow() { GetWindow("AkiBT Service"); diff --git a/Editor/Core/GraphView/Node/SubtreeNode.cs b/Editor/Core/GraphView/Node/SubtreeNode.cs index c56c9dd..27bfeff 100644 --- a/Editor/Core/GraphView/Node/SubtreeNode.cs +++ b/Editor/Core/GraphView/Node/SubtreeNode.cs @@ -23,7 +23,7 @@ protected override void OnRestore() return; } subtree = node.subtree; - title = $"Subtree {subtree.name}"; + title = $"Subtree: {subtree.name}"; foreach (var variable in subtree.GetBehaviorTree().SharedVariables) { // not add if exist diff --git a/Editor/Kurisu.AkiBT.Editor.asmdef b/Editor/Core/Kurisu.AkiBT.Editor.asmdef similarity index 100% rename from Editor/Kurisu.AkiBT.Editor.asmdef rename to Editor/Core/Kurisu.AkiBT.Editor.asmdef diff --git a/Editor/Kurisu.AkiBT.Editor.asmdef.meta b/Editor/Core/Kurisu.AkiBT.Editor.asmdef.meta similarity index 100% rename from Editor/Kurisu.AkiBT.Editor.asmdef.meta rename to Editor/Core/Kurisu.AkiBT.Editor.asmdef.meta diff --git a/Editor/Core/Model/BehaviorTreeSetting.cs b/Editor/Core/Model/BehaviorTreeSetting.cs index a4af85d..90749eb 100644 --- a/Editor/Core/Model/BehaviorTreeSetting.cs +++ b/Editor/Core/Model/BehaviorTreeSetting.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using UnityEditor; +using UnityEditorInternal; using UnityEngine; using UnityEngine.UIElements; namespace Kurisu.AkiBT.Editor @@ -23,8 +24,8 @@ internal class EditorSetting } public class BehaviorTreeSetting : ScriptableObject { - public const string Version = "v1.4.8"; - private const string k_BehaviorTreeSettingsPath = "Assets/AkiBTSetting.asset"; + public const string Version = "v1.5.0"; + private const string k_BehaviorTreeSettingsPath = "ProjectSettings/AkiBTSetting.asset"; private const string k_UserServiceSettingPath = "Assets/AkiBTUserServiceData.asset"; private const string GraphFallBackPath = "AkiBT/Graph"; private const string InspectorFallBackPath = "AkiBT/Inspector"; @@ -103,27 +104,26 @@ public StyleSheet GetNodeStyle(string editorName) var editorSetting = settings.First(x => x.EditorName.Equals(editorName)); return editorSetting.nodeStyleSheet != null ? editorSetting.nodeStyleSheet : Resources.Load(NodeFallBackPath); } - public (string[], string[]) GetMask(string editorName) + private static readonly string[] internalNotShowGroups = new string[1] { "Hidden" }; + public (string[] showGroups, string[] notShowGroups) GetMask(string editorName) { - if (settings == null || settings.Length == 0 || !settings.Any(x => x.EditorName.Equals(editorName))) return (null, null); + if (settings == null || settings.Length == 0 || !settings.Any(x => x.EditorName.Equals(editorName))) return (null, internalNotShowGroups); var editorSetting = settings.First(x => x.EditorName.Equals(editorName)); - return (editorSetting.ShowGroups, editorSetting.NotShowGroups.Concat(new string[1] { "Hidden" }).ToArray()); + return (editorSetting.ShowGroups, editorSetting.NotShowGroups.Concat(internalNotShowGroups).ToArray()); } public static BehaviorTreeSetting GetOrCreateSettings() { - var guids = AssetDatabase.FindAssets($"t:{nameof(BehaviorTreeSetting)}"); BehaviorTreeSetting setting = null; - if (guids.Length == 0) - { - setting = CreateInstance(); - Debug.Log($"AkiBT Setting saving path : {k_BehaviorTreeSettingsPath}"); - AssetDatabase.CreateAsset(setting, k_BehaviorTreeSettingsPath); - AssetDatabase.SaveAssets(); - } - else setting = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guids[0])); + var arr = InternalEditorUtility.LoadSerializedFileAndForget(k_BehaviorTreeSettingsPath); + setting = arr.Length > 0 ? arr[0] as BehaviorTreeSetting : setting != null ? setting : CreateInstance(); return setting; } + public void Save(bool saveAsText = true) + { + InternalEditorUtility.SaveToSerializedFileAndForget(new[] { this }, k_BehaviorTreeSettingsPath, saveAsText); + } + internal static SerializedObject GetSerializedSettings() { return new SerializedObject(GetOrCreateSettings()); @@ -132,7 +132,6 @@ internal static SerializedObject GetSerializedSettings() internal class BehaviorTreeSettingsProvider : SettingsProvider { - private SerializedObject m_Settings; private class Styles { public static GUIContent GraphEditorSettingStyle = new("Graph Editor Setting"); @@ -140,18 +139,24 @@ private class Styles public static GUIContent SerializeEditorDataStyle = new("Serialize Editor Data", "Serialize node editor data when use json serialization, turn off to decrease file size"); } public BehaviorTreeSettingsProvider(string path, SettingsScope scope = SettingsScope.User) : base(path, scope) { } + private BehaviorTreeSetting setting; + private SerializedObject serializedObject; public override void OnActivate(string searchContext, VisualElement rootElement) { - m_Settings = BehaviorTreeSetting.GetSerializedSettings(); + setting = BehaviorTreeSetting.GetOrCreateSettings(); + serializedObject = new(setting); } public override void OnGUI(string searchContext) { GUILayout.BeginVertical("Editor Settings", GUI.skin.box); GUILayout.Space(EditorGUIUtility.singleLineHeight); - EditorGUILayout.PropertyField(m_Settings.FindProperty("settings"), Styles.GraphEditorSettingStyle); - EditorGUILayout.PropertyField(m_Settings.FindProperty("autoLayoutSiblingDistance"), Styles.LayoutDistanceStyle); - EditorGUILayout.PropertyField(m_Settings.FindProperty("jsonSerializeEditorData"), Styles.SerializeEditorDataStyle); - m_Settings.ApplyModifiedPropertiesWithoutUndo(); + EditorGUILayout.PropertyField(serializedObject.FindProperty("settings"), Styles.GraphEditorSettingStyle); + EditorGUILayout.PropertyField(serializedObject.FindProperty("autoLayoutSiblingDistance"), Styles.LayoutDistanceStyle); + EditorGUILayout.PropertyField(serializedObject.FindProperty("jsonSerializeEditorData"), Styles.SerializeEditorDataStyle); + if (serializedObject.ApplyModifiedPropertiesWithoutUndo()) + { + setting.Save(); + } GUILayout.EndVertical(); } [SettingsProvider] diff --git a/Editor/Core/SearchWindow/NodeSearchWindowProvider.cs b/Editor/Core/SearchWindow/NodeSearchWindowProvider.cs index 9afdf3e..b9d4396 100644 --- a/Editor/Core/SearchWindow/NodeSearchWindowProvider.cs +++ b/Editor/Core/SearchWindow/NodeSearchWindowProvider.cs @@ -116,8 +116,8 @@ List ISearchWindowProvider.CreateSearchTree(SearchWindowContext entries.Add(new SearchTreeGroupEntry(new GUIContent($"Select {typeof(T).Name}"), 0)); List nodeTypes = SubclassSearchUtility.FindSubClassTypes(typeof(T)); - var groups = nodeTypes.GroupsByAkiGroup();//按AkiGroup进行分类 - nodeTypes = nodeTypes.Except(groups.SelectMany(x => x)).ToList();//去除被分类的部分 + var groups = nodeTypes.GroupsByAkiGroup(); + nodeTypes = nodeTypes.Except(groups.SelectMany(x => x)).ToList(); groups = groups.SelectGroup(showGroups).ExceptGroup(notShowGroups); foreach (var group in groups) { diff --git a/Editor/Core/SearchWindow/NodeTypeSearchWindow.cs b/Editor/Core/SearchWindow/NodeTypeSearchWindow.cs index 15ef0ef..f1cafe2 100644 --- a/Editor/Core/SearchWindow/NodeTypeSearchWindow.cs +++ b/Editor/Core/SearchWindow/NodeTypeSearchWindow.cs @@ -26,6 +26,7 @@ List ISearchWindowProvider.CreateSearchTree(SearchWindowContext List nodeTypes = SubclassSearchUtility.FindSubClassTypes(_Types); var groups = nodeTypes.GroupsByAkiGroup(); ; nodeTypes = nodeTypes.Except(groups.SelectMany(x => x)).ToList(); + groups = groups.ExceptGroup(new string[1] { "Hidden" }); foreach (var _type in _Types) { entries.Add(new SearchTreeGroupEntry(new GUIContent($"Select {_type.Name}"), 1)); diff --git a/Editor/Core/Utility/APIUpdateConfig.cs b/Editor/Core/Utility/APIUpdateConfig.cs deleted file mode 100644 index 11e9e76..0000000 --- a/Editor/Core/Utility/APIUpdateConfig.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using UnityEditor; -using UnityEditor.Experimental.GraphView; -using UnityEngine; -namespace Kurisu.AkiBT.Editor -{ - [CreateAssetMenu(fileName = "APIUpdateConfig", menuName = "AkiBT/APIUpdateConfig")] - public class APIUpdateConfig : ScriptableObject - { - [Serializable] - public class SerializeType - { - private Type type; - public Type Type => type ??= Type.GetType(assemblyQualifiedName); - public string assemblyQualifiedName; - public string GetFullTypeName() - { - return $"{Type.Assembly.GetName().Name} {Type.FullName}"; - } - public SerializeType() { } - public SerializeType(Type type) - { - assemblyQualifiedName = type.AssemblyQualifiedName; - } - } - [Serializable] - public class Pair - { - public SerializeType sourceType; - public SerializeType targetType; - public Pair() { } - public Pair(Type sourceType, Type targetType) - { - this.sourceType = new SerializeType(sourceType); - this.targetType = new SerializeType(targetType); - } - } - [field: SerializeField] - public Pair[] Pairs { get; set; } - } - [CustomPropertyDrawer(typeof(APIUpdateConfig.SerializeType))] - public class SerializeTypeDrawer : PropertyDrawer - { - private const string NullType = "Null"; - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) - { - EditorGUI.BeginProperty(position, label, property); - var reference = property.FindPropertyRelative("assemblyQualifiedName"); - var type = Type.GetType(reference.stringValue); - string id = type != null ? $"{type.Assembly.GetName().Name} {type.Namespace} {type.Name}" : NullType; - if (EditorGUI.DropdownButton(position, new GUIContent(id), FocusType.Keyboard)) - { - var provider = ScriptableObject.CreateInstance(); - provider.Initialize((type) => - { - reference.stringValue = type?.AssemblyQualifiedName ?? NullType; - property.serializedObject.ApplyModifiedProperties(); - }); - SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)), provider); - } - EditorGUI.EndProperty(); - } - } - [CustomEditor(typeof(APIUpdateConfig))] - public class APIUpdateConfigEditor : UnityEditor.Editor - { - public override void OnInspectorGUI() - { - base.OnInspectorGUI(); - if (GUILayout.Button("Update API")) - { - APIUpdater.UpdateAPI(typeof(BehaviorTreeAsset), target as APIUpdateConfig); - } - } - } -} diff --git a/Samples~/Basic Example.meta b/Editor/DSL.meta similarity index 77% rename from Samples~/Basic Example.meta rename to Editor/DSL.meta index 3ff99a4..32ac9f0 100644 --- a/Samples~/Basic Example.meta +++ b/Editor/DSL.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 453f3429931886e49ad8f252701c737f +guid: 31d588c22917a4042a23d6eb15116025 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Editor/DSL/CodeSplitter.cs b/Editor/DSL/CodeSplitter.cs new file mode 100644 index 0000000..9a4d59c --- /dev/null +++ b/Editor/DSL/CodeSplitter.cs @@ -0,0 +1,161 @@ +using UnityEngine; +using UnityEditor; +using System; +using System.IO; +using System.Reflection; +using System.Linq; +using Newtonsoft.Json; +namespace Kurisu.AkiBT.DSL.Editor +{ + /// + /// Tool to split fields and function, header file can be provided for UGC side. + /// User do not need to know implementation detail. + /// + public class CodeSplitter : EditorWindow + { + private class GeneratorSetting + { + public bool convertFieldsToPublic; + public string scriptFolderPath = string.Empty; + } + private GeneratorSetting setting; + private static string KeyName => Application.productName + "_AkiBT_DSL_SplitterSetting"; + [MenuItem("Tools/AkiBT/DSL/Code Splitter", priority = 20)] + private static void GetWindow() + { + GetWindowWithRect(new Rect(0, 0, 400, 200)); + } + private void OnEnable() + { + var data = EditorPrefs.GetString(KeyName); + setting = JsonConvert.DeserializeObject(data); + setting ??= new GeneratorSetting(); + } + private void OnDisable() + { + EditorPrefs.SetString(KeyName, JsonConvert.SerializeObject(setting)); + } + private void OnGUI() + { + setting.convertFieldsToPublic = EditorGUILayout.Toggle("Convert Fields To Public", setting.convertFieldsToPublic); + if (GUILayout.Button("Select Script Folder")) + { + setting.scriptFolderPath = EditorUtility.OpenFolderPanel("Select Script Folder", "", ""); + } + GUILayout.Label($"Path: {setting.scriptFolderPath}", new GUIStyle(GUI.skin.label) { wordWrap = true }); + if (GUILayout.Button("Split Code")) + { + SplitCode(); + } + if (GUILayout.Button("Restore Code")) + { + RestoreCode(); + } + } + private void SplitCode() + { + if (string.IsNullOrEmpty(setting.scriptFolderPath)) + { + Debug.LogError("Script folder path is not selected!"); + return; + } + string[] scriptFiles = Directory.GetFiles(setting.scriptFolderPath, "*.cs", SearchOption.AllDirectories); + var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()); + foreach (string scriptFile in scriptFiles) + { + string scriptContent = File.ReadAllText(scriptFile).TrimEnd(); + //ScriptName should be class name + string className = Path.GetFileNameWithoutExtension(scriptFile); + string namespaceName = GetNamespace(scriptContent); + //Find last field index + Type scriptType = types.FirstOrDefault(x => x.Name == className && x.Namespace == namespaceName); + if (scriptType == null) + { + Debug.Log($"Can not find type from {namespaceName}.{className}"); + continue; + } + FieldInfo[] fields = scriptType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + //Suppose class is public + int classIndex = scriptContent.IndexOf("public class "); + if (classIndex == -1) + { + Debug.Log($"{namespaceName}.{className} is not public, generation was skipped"); + continue; + } + int namespaceEndIndex = scriptContent.IndexOf('{'); + int endIndex = GetLastFieldIndex(fields, scriptContent); + if (endIndex == -1) + { + //No fields contained => use class '{' index + endIndex = scriptContent.IndexOf('{', namespaceEndIndex + 1) + 1; + } + string fieldContent = scriptContent[..endIndex]; + if (setting.convertFieldsToPublic) + { + fieldContent = fieldContent.Replace("private ", "public ").Replace("internal ", "public ").Replace("protected ", "public "); + } + string methodContent = scriptContent[(endIndex + 1)..]; + //Replace to partial class + string headerScriptContent = fieldContent.Replace("public class", "public partial class") + "\n}\n}"; + int indent = scriptContent.LastIndexOf('}', scriptContent.Length - 2) - scriptContent.LastIndexOf('\n', scriptContent.Length - 3) - 1; + string bodyScriptContent = scriptContent[..(namespaceEndIndex + 1)] + "\n" + new string(' ', indent) + "public partial class " + className + "\n" + new string(' ', indent) + "{\n" + methodContent; + string headerScriptPath = Path.GetDirectoryName(scriptFile) + "/" + className + "_Header.cs"; + string bodyScriptPath = Path.GetDirectoryName(scriptFile) + "/" + className + ".cs"; + string backUpPath = Path.GetDirectoryName(scriptFile) + "/" + className + "_Backup.txt"; + //Add backup for original script + File.WriteAllText(backUpPath, scriptContent); + File.WriteAllText(headerScriptPath, headerScriptContent); + File.WriteAllText(bodyScriptPath, bodyScriptContent); + } + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + Debug.Log("Code generation complete!"); + } + private void RestoreCode() + { + if (string.IsNullOrEmpty(setting.scriptFolderPath)) + { + Debug.LogError("Script folder path is not selected!"); + return; + } + string[] headerFiles = Directory.GetFiles(setting.scriptFolderPath, "*_Header.cs", SearchOption.AllDirectories); + foreach (var file in headerFiles) File.Delete(file); + string[] scriptFiles = Directory.GetFiles(setting.scriptFolderPath, "*_Backup.txt", SearchOption.AllDirectories); + foreach (var file in scriptFiles) + { + string fileName = Path.GetFileNameWithoutExtension(file)[..^7]; + string filePath = Path.Combine(Path.GetDirectoryName(file), $"{fileName}.cs"); + var scriptContent = File.ReadAllText(file); + File.WriteAllText(filePath, scriptContent); + File.Delete(file); + } + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + Debug.Log("Code restoration complete!"); + } + private static string GetNamespace(string scriptContent) + { + int namespaceIndex = scriptContent.IndexOf("namespace "); + int namespaceEndIndex = scriptContent.IndexOf('{'); + return scriptContent[(namespaceIndex + 10)..namespaceEndIndex].TrimEnd(); + } + private static int GetLastFieldIndex(FieldInfo[] fieldInfos, string scriptContent) + { + int endIndex = -1; + foreach (FieldInfo fieldInfo in fieldInfos) + { + int index = scriptContent.IndexOf(" " + fieldInfo.Name + ";"); + if (index != -1) + { + index += fieldInfo.Name.Length + 2; + if (index > endIndex) + { + endIndex = index; + } + } + } + return endIndex; + } + + } +} \ No newline at end of file diff --git a/Runtime/Core/Utility/SharedVariableHelper.cs.meta b/Editor/DSL/CodeSplitter.cs.meta similarity index 83% rename from Runtime/Core/Utility/SharedVariableHelper.cs.meta rename to Editor/DSL/CodeSplitter.cs.meta index aa922e8..73ffe09 100644 --- a/Runtime/Core/Utility/SharedVariableHelper.cs.meta +++ b/Editor/DSL/CodeSplitter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f1c9aa05b3771f449b16ae6a094a62f2 +guid: e4c8c6f98b11aba4495739bae01610c6 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Editor/DSL/CompilerEditorWindow.cs b/Editor/DSL/CompilerEditorWindow.cs new file mode 100644 index 0000000..faca5a6 --- /dev/null +++ b/Editor/DSL/CompilerEditorWindow.cs @@ -0,0 +1,377 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using Kurisu.AkiBT.Editor; +using Newtonsoft.Json; +using UnityEditor; +using UnityEngine; +namespace Kurisu.AkiBT.DSL.Editor +{ + public class CompilerEditorWindow : EditorWindow + { + private class CompilerSetting + { + public bool enableMask; + public string editorName = "AkiBT"; + public string inputCode = string.Empty; + public string registryName = "NodeTypeRegistry"; + } + private string InputCode { get => setting.inputCode; set => setting.inputCode = value; } + private BehaviorTreeAsset _outputTree; + private Vector2 m_ScrollPosition; + private bool EnableMask { get => setting.enableMask; set => setting.enableMask = value; } + private string EditorName { get => setting.editorName; set => setting.editorName = value; } + private string RegistryNameName { get => setting.registryName; set => setting.registryName = value; } + private static string KeyName => Application.productName + "_AkiBT_DSL_CompilerSetting"; + private CompilerSetting setting; + public delegate Vector2 BeginVerticalScrollViewFunc(Vector2 scrollPosition, bool alwaysShowVertical, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options); + static BeginVerticalScrollViewFunc s_func; + static BeginVerticalScrollViewFunc BeginVerticalScrollView + { + get + { + if (s_func == null) + { + var methods = typeof(EditorGUILayout).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).Where(x => x.Name == "BeginVerticalScrollView").ToArray(); + var method = methods.First(x => x.GetParameters()[1].ParameterType == typeof(bool)); + s_func = (BeginVerticalScrollViewFunc)method.CreateDelegate(typeof(BeginVerticalScrollViewFunc)); + } + return s_func; + } + } + private GUIStyle textAreaStyle; + private GUIStyle labelStyle; + private int state = 2; + private int mTab; + [MenuItem("Tools/AkiBT/DSL/DSL Compiler", priority = 10)] + public static void OpenEditor() + { + var window = GetWindow("AkiBT DSL Compiler"); + window.maxSize = new Vector2(600, 800); + } + private void OnEnable() + { + var data = EditorPrefs.GetString(KeyName); + setting = JsonConvert.DeserializeObject(data); + setting ??= new CompilerSetting(); + } + private void OnDisable() + { + EditorPrefs.SetString(KeyName, JsonConvert.SerializeObject(setting)); + } + private void GatherStyle() + { + textAreaStyle = new GUIStyle(GUI.skin.textArea) { wordWrap = true }; + labelStyle = new GUIStyle(GUI.skin.label) { richText = true }; + } + private void OnGUI() + { + GatherStyle(); + int newTab = GUILayout.Toolbar(mTab, new string[] { "Compile", "Decompile" }); + if (newTab != mTab) + { + mTab = newTab; + state = 2; + } + m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box"); + GUILayout.Label("Input Code"); + InputCode = EditorGUILayout.TextArea(InputCode, textAreaStyle, GUILayout.MaxHeight(700)); + EditorGUILayout.EndScrollView(); + GUILayout.FlexibleSpace(); + switch (mTab) + { + case 0: + DrawCompile(); + break; + case 1: + DrawDecompile(); + break; + } + DrawToolbar(); + } + private void DrawCompile() + { + GUILayout.Label("Output Tree"); + GUI.enabled = false; + EditorGUILayout.ObjectField(_outputTree, typeof(BehaviorTreeComponent), false); + GUI.enabled = true; + var orgColor = GUI.backgroundColor; + GUI.backgroundColor = new Color(140 / 255f, 160 / 255f, 250 / 255f); + if (GUILayout.Button("Compile", GUILayout.MinHeight(25))) + { + EditorApplication.update += DelayCall; + } + GUI.backgroundColor = new Color(253 / 255f, 163 / 255f, 255 / 255f); + if (state == 1 && GUILayout.Button("Export", GUILayout.MinHeight(25))) + { + string path = EditorUtility.OpenFolderPanel("Select saving path", Application.dataPath, ""); + if (string.IsNullOrEmpty(path)) return; + var savePath = path.Replace(Application.dataPath, string.Empty); + string outPutPath = $"Assets/{savePath}/ConvertTreeSO.asset"; + AssetDatabase.CreateAsset(_outputTree, outPutPath); + AssetDatabase.SaveAssets(); + Log($"BehaviorTreeSO saved succeed! File Path:{outPutPath}"); + GUIUtility.ExitGUI(); + } + GUI.backgroundColor = orgColor; + DrawCompileResult(state); + } + private void DelayCall() + { + EditorApplication.update -= DelayCall; + state = 0; + state = Compile(); + } + private void DrawDecompile() + { + DrawDragAndDrop(); + DrawDecompileResult(state); + var orgColor = GUI.backgroundColor; + GUI.backgroundColor = new Color(140 / 255f, 160 / 255f, 250 / 255f); + if (GUILayout.Button("Decompile All", GUILayout.MinHeight(25))) + { + string path = EditorUtility.OpenFolderPanel("Choose decompile files saving path", Application.dataPath, ""); + if (string.IsNullOrEmpty(path)) return; + //Using AkiBT Service + var serviceData = BehaviorTreeSetting.GetOrCreateSettings().ServiceData; + serviceData.ForceSetUp(); + var decompiler = new Decompiler(); + foreach (var pair in serviceData.serializationCollection.serializationPairs) + { + if (pair.behaviorTreeAsset != null) + { + string data; + try + { + data = decompiler.Decompile(pair.behaviorTreeAsset); + } + catch + { + LogError($"Decompile failed with {pair.behaviorTreeAsset.name}"); + continue; + } + string folderPath = path + $"/{pair.behaviorTreeAsset.GetType().Name}"; + if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath); + string savePath = $"{folderPath}/{pair.behaviorTreeAsset.name}_DSL.json"; + File.WriteAllText(savePath, data); + } + } + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + GUIUtility.ExitGUI(); + } + GUI.backgroundColor = orgColor; + } + private void DrawDragAndDrop() + { + + GUIStyle StyleBox = new(GUI.skin.box) + { + alignment = TextAnchor.MiddleCenter, + fontStyle = FontStyle.Italic, + fontSize = 12 + }; + GUI.skin.box = StyleBox; + Rect myRect = GUILayoutUtility.GetRect(0, 50, GUILayout.ExpandWidth(true)); + GUI.Box(myRect, "Drag and Drop BehaviorTree to this Box!", StyleBox); + { + if (Event.current.type == EventType.DragUpdated) + { + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + Event.current.Use(); + + } + if (Event.current.type == EventType.DragPerform) + { + if (DragAndDrop.objectReferences.Length > 0) + { + state = 0; + if (DragAndDrop.objectReferences[0] is GameObject gameObject) + state = Decompile(gameObject.GetComponent()); + else if (DragAndDrop.objectReferences[0] is IBehaviorTreeContainer behaviorTree) + state = Decompile(behaviorTree); + else + state = -1; + } + } + } + } + private void DrawToolbar() + { + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.Label("Registry Name", labelStyle); + RegistryNameName = GUILayout.TextField(RegistryNameName, GUILayout.MinWidth(100)); + GUILayout.Label("Using Editor Mask", labelStyle); + GUI.enabled = EnableMask; + EditorName = GUILayout.TextField(EditorName, GUILayout.MinWidth(50)); + GUI.enabled = true; + EnableMask = EditorGUILayout.ToggleLeft(string.Empty, EnableMask, GUILayout.Width(20)); + var orgColor = GUI.backgroundColor; + GUI.backgroundColor = new Color(253 / 255f, 163 / 255f, 255 / 255f); + if (GUILayout.Button("Create Node Type Registry")) + { + EditorApplication.delayCall += WriteNodeTypeRegistry; + } + GUI.backgroundColor = orgColor; + GUILayout.EndHorizontal(); + } + private void DrawCompileResult(int state) + { + if (state == 1) + { + GUILayout.Label("Compiler : Compile Succeed!", labelStyle); + } + if (state == 0) + { + GUILayout.Label("Compiler : Compile Failed!", labelStyle); + } + if (state == -1) + { + GUILayout.Label("Compiler : Input Code Is Empty!", labelStyle); + } + if (state == -2) + { + GUILayout.Label("Compiler : Type Dictionary has not generated!", labelStyle); + } + } + private void DrawDecompileResult(int state) + { + if (state == 1) + { + GUILayout.Label("AkiBTDecompiler : Decompile Succeed!", labelStyle); + } + if (state == 0) + { + GUILayout.Label("AkiBTDecompiler : Decompile Failed!", labelStyle); + } + if (state == -1) + { + GUILayout.Label("AkiBTDecompiler : Input Object's type is not supported!", labelStyle); + } + if (state == -2) + { + GUILayout.Label("AkiBTDecompiler : Type Dictionary has not generated!", labelStyle); + } + } + private int Compile() + { + string fileInStreaming = $"{Application.streamingAssetsPath}/{RegistryNameName}.json"; + if (!File.Exists(fileInStreaming)) + { + return -2; + } + if (string.IsNullOrEmpty(InputCode)) + { + return -1; + } + var bt = new Compiler(fileInStreaming).Verbose(true).Compile(InputCode); + _outputTree = CreateInstance(); + _outputTree.SetBehaviorTreeData(bt.GetData()); + return 1; + } + private int Decompile(IBehaviorTreeContainer behaviorTreeContainer) + { + string fileInStreaming = $"{Application.streamingAssetsPath}/{RegistryNameName}.json"; + if (!File.Exists(fileInStreaming)) + { + return -2; + } + if (behaviorTreeContainer == null) return -1; + InputCode = new Decompiler().Decompile(behaviorTreeContainer); + return 1; + } + private void WriteNodeTypeRegistry() + { + IEnumerable list = SubclassSearchUtility.FindSubClassTypes(typeof(NodeBehavior)); + string[] showGroups = null; + string[] notShowGroups = null; + if (EnableMask) (showGroups, notShowGroups) = BehaviorTreeSetting.GetOrCreateSettings().GetMask(EditorName); + if (showGroups != null) + { + for (int i = 0; i < showGroups.Length; i++) Log($"Showing Group:{showGroups[i]}"); + } + if (notShowGroups != null) + { + for (int i = 0; i < notShowGroups.Length; i++) Log($"Not Showing Group:{notShowGroups[i]}"); + } + var groups = list.GroupsByAkiGroup(); + list = list.Except(groups.SelectMany(x => x)).ToList(); + groups = groups.SelectGroup(showGroups).ExceptGroup(notShowGroups); + list = list.Concat(groups.SelectMany(x => x).Distinct()).Concat(SubclassSearchUtility.FindSubClassTypes(typeof(SharedVariable))); ; + var registry = new NodeTypeRegistry(); + foreach (var type in list) + { + AddTypeInfo(registry, type); + } + string path = $"{Application.streamingAssetsPath}/{RegistryNameName}.json"; + if (!File.Exists(Application.streamingAssetsPath)) + { + Directory.CreateDirectory(Application.streamingAssetsPath); + } + //Write to file + File.WriteAllText(path, JsonConvert.SerializeObject(registry, Formatting.Indented), System.Text.Encoding.UTF8); + Log($"Create node type registry succeed, saving path: {path}"); + } + private static void AddTypeInfo(NodeTypeRegistry dict, Type type) + { + string path = type.Name; + // Generate a node path as simple as possible + if (dict.TryGetNode(path, out _)) + { + string newPath = $"{type.Namespace}.{path}"; + Log($"{path} already exits, append namespace to path: {newPath}"); + path = newPath; + if (dict.TryGetNode(path, out _)) + { + newPath = $"{type.Assembly.GetName().Name}.{path}"; + Log($"{path} already exits, append assembly name to path: {newPath}"); + path = newPath; + } + } + dict.SetNode(path, GenerateTypeInfo(type)); + } + private static NodeInfo GenerateTypeInfo(Type type) + { + var enumDict = new Dictionary(); + var info = new NodeInfo + { + className = type.Name, + ns = type.Namespace, + asm = type.Assembly.GetName().Name + }; + if (type.IsSubclassOf(typeof(SharedVariable))) + { + info.isVariable = true; + return info; + } + info.isVariable = false; + var properties = NodeTypeRegistry.GetAllFields(type).ToList(); + if (properties.Count == 0) return info; + info.properties = new(); + properties.ForEach((p) => + { + var label = p.GetCustomAttribute(typeof(AkiLabelAttribute), false) as AkiLabelAttribute; + PropertyInfo propertyInfo; + info.properties.Add(propertyInfo = new PropertyInfo() + { + name = p.Name, + label = label?.Title ?? p.Name, + // Get a fast field type + fieldType = NodeTypeRegistry.GetFieldType(p.FieldType) + }); + }); + return info; + } + private static void Log(string message) + { + Debug.Log($"Compiler :{message}"); + } + private static void LogError(string message) + { + Debug.Log($"Compiler :{message}"); + } + } +} \ No newline at end of file diff --git a/Editor/DSL/CompilerEditorWindow.cs.meta b/Editor/DSL/CompilerEditorWindow.cs.meta new file mode 100644 index 0000000..63bdad9 --- /dev/null +++ b/Editor/DSL/CompilerEditorWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bee963a07bd7fce4d84ccb2b8b2689e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/DSL/Kurisu.AkiBT.DSL.Editor.asmdef b/Editor/DSL/Kurisu.AkiBT.DSL.Editor.asmdef new file mode 100644 index 0000000..f9d1c74 --- /dev/null +++ b/Editor/DSL/Kurisu.AkiBT.DSL.Editor.asmdef @@ -0,0 +1,20 @@ +{ + "name": "Kurisu.AkiBT.DSL.Editor", + "rootNamespace": "Kurisu.AkiBT.DSL.Editor", + "references": [ + "GUID:bf89a8bf6625fad4eb6879c01e73541d", + "GUID:ea13d7331a0df234f85e126949843072", + "GUID:f107c6017d59d64439a1cf5a67861cd3" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Editor/DSL/Kurisu.AkiBT.DSL.Editor.asmdef.meta b/Editor/DSL/Kurisu.AkiBT.DSL.Editor.asmdef.meta new file mode 100644 index 0000000..ac4b44d --- /dev/null +++ b/Editor/DSL/Kurisu.AkiBT.DSL.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c0d5ed6b85f2c1d4c8e3cefb06e851dd +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Resources/AkiBT/Icon.png b/Editor/Resources/AkiBT/Icon.png index e741b6c..9c9af04 100644 Binary files a/Editor/Resources/AkiBT/Icon.png and b/Editor/Resources/AkiBT/Icon.png differ diff --git a/Extend/Animator/CrossFade.cs b/Extend/Animator/CrossFade.cs index 7e92fa9..c5782e4 100644 --- a/Extend/Animator/CrossFade.cs +++ b/Extend/Animator/CrossFade.cs @@ -11,6 +11,10 @@ public class CrossFade : AnimatorAction public SharedFloat normalizedTimeOffset = new(float.NegativeInfinity); protected override Status OnUpdate() { + if (Animator == null) + { + return Status.Failure; + } Animator.CrossFade(stateName.Value, normalizedTransitionDuration.Value, layer.Value, normalizedTimeOffset.Value); return Status.Success; } diff --git a/Extend/Animator/Play.cs b/Extend/Animator/Play.cs index 6c52804..e51b2f7 100644 --- a/Extend/Animator/Play.cs +++ b/Extend/Animator/Play.cs @@ -9,6 +9,10 @@ public class Play : AnimatorAction public SharedInt layer = new(-1); protected override Status OnUpdate() { + if (Animator == null) + { + return Status.Failure; + } Animator.Play(stateName.Value, layer.Value); return Status.Success; } diff --git a/Samples~/Builder Example.meta b/Extend/NavmeshAgent.meta similarity index 77% rename from Samples~/Builder Example.meta rename to Extend/NavmeshAgent.meta index 35481c2..7601480 100644 --- a/Samples~/Builder Example.meta +++ b/Extend/NavmeshAgent.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 43d054291d8e99a48b6858d4e04715cb +guid: 89272dfad86f6dd439f91576ba3b79ed folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Extend/NavmeshAgent/GetAcceleration.cs b/Extend/NavmeshAgent/GetAcceleration.cs new file mode 100644 index 0000000..6d2118f --- /dev/null +++ b/Extend/NavmeshAgent/GetAcceleration.cs @@ -0,0 +1,29 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: GetAcceleration")] + [AkiInfo("Action: Gets the maximum acceleration of an agent as it follows a path, given in units / sec^2.")] + public class GetAcceleration : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + [ForceShared, Tooltip("The NavMeshAgent acceleration")] + public SharedFloat storeValue; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + storeValue.Value = agent.Value.acceleration; + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/GetAcceleration.cs.meta b/Extend/NavmeshAgent/GetAcceleration.cs.meta new file mode 100644 index 0000000..89d5a60 --- /dev/null +++ b/Extend/NavmeshAgent/GetAcceleration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4fffb3a422506e542a9f0ffecafeab25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/GetAngularSpeed.cs b/Extend/NavmeshAgent/GetAngularSpeed.cs new file mode 100644 index 0000000..c326e4a --- /dev/null +++ b/Extend/NavmeshAgent/GetAngularSpeed.cs @@ -0,0 +1,29 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: GetAngularSpeed")] + [AkiInfo("Action: Maximum turning speed in (deg/s) while following a path.")] + public class GetAngularSpeed : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + [ForceShared, Tooltip("The NavMeshAgent angularSpeed")] + public SharedFloat storeValue; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + storeValue.Value = agent.Value.angularSpeed; + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/GetAngularSpeed.cs.meta b/Extend/NavmeshAgent/GetAngularSpeed.cs.meta new file mode 100644 index 0000000..fb5403e --- /dev/null +++ b/Extend/NavmeshAgent/GetAngularSpeed.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b96946c4cad32d42b947d6b053ff8e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/GetDestination.cs b/Extend/NavmeshAgent/GetDestination.cs new file mode 100644 index 0000000..afbe87d --- /dev/null +++ b/Extend/NavmeshAgent/GetDestination.cs @@ -0,0 +1,29 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: GetDestination")] + [AkiInfo("Action: Gets or attempts to set the destination of the agent in world-space units.")] + public class GetDestination : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + [ForceShared, Tooltip("The NavMeshAgent destination")] + public SharedVector3 storeValue; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + storeValue.Value = agent.Value.destination; + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/GetDestination.cs.meta b/Extend/NavmeshAgent/GetDestination.cs.meta new file mode 100644 index 0000000..9e2d57d --- /dev/null +++ b/Extend/NavmeshAgent/GetDestination.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e168992374e76314cb7fe25fa4246b4e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/GetIsStopped.cs b/Extend/NavmeshAgent/GetIsStopped.cs new file mode 100644 index 0000000..62b5d72 --- /dev/null +++ b/Extend/NavmeshAgent/GetIsStopped.cs @@ -0,0 +1,29 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: GetIsStopped")] + [AkiInfo("Action: Get property holds the stop or resume condition of the NavMesh agent.")] + public class GetIsStopped : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + [ForceShared, Tooltip("The NavMeshAgent isStopped")] + public SharedBool storeValue; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + storeValue.Value = agent.Value.isStopped; + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/GetIsStopped.cs.meta b/Extend/NavmeshAgent/GetIsStopped.cs.meta new file mode 100644 index 0000000..7d3c0a5 --- /dev/null +++ b/Extend/NavmeshAgent/GetIsStopped.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ad1eb0015ae62154eabaa56793d3e259 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/GetRemainingDistance.cs b/Extend/NavmeshAgent/GetRemainingDistance.cs new file mode 100644 index 0000000..b04e60e --- /dev/null +++ b/Extend/NavmeshAgent/GetRemainingDistance.cs @@ -0,0 +1,29 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: GetRemainingDistance")] + [AkiInfo("Action: Get the distance between the agent's position and the destination on the current path.")] + public class GetRemainingDistance : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + [ForceShared, Tooltip("The NavMeshAgent remainingDistance")] + public SharedFloat storeValue; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + storeValue.Value = agent.Value.remainingDistance; + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/GetRemainingDistance.cs.meta b/Extend/NavmeshAgent/GetRemainingDistance.cs.meta new file mode 100644 index 0000000..da910c3 --- /dev/null +++ b/Extend/NavmeshAgent/GetRemainingDistance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26570384421df964589e76dd70af1b3a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/GetSpeed.cs b/Extend/NavmeshAgent/GetSpeed.cs new file mode 100644 index 0000000..191b868 --- /dev/null +++ b/Extend/NavmeshAgent/GetSpeed.cs @@ -0,0 +1,29 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: GetSpeed")] + [AkiInfo("Action: Get the maximum movement speed when following a path.")] + public class GetSpeed : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + [ForceShared, Tooltip("The NavMeshAgent remainingDistance")] + public SharedFloat storeValue; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + storeValue.Value = agent.Value.speed; + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/GetSpeed.cs.meta b/Extend/NavmeshAgent/GetSpeed.cs.meta new file mode 100644 index 0000000..3e4a510 --- /dev/null +++ b/Extend/NavmeshAgent/GetSpeed.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cf0b477ba2c28e945a8922016db820d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/IsStopped.cs b/Extend/NavmeshAgent/IsStopped.cs new file mode 100644 index 0000000..ca828eb --- /dev/null +++ b/Extend/NavmeshAgent/IsStopped.cs @@ -0,0 +1,28 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: IsStopped")] + [AkiInfo("Conditional: Whether NavMeshAgent is stopped?")] + public class IsStopped : Conditional + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + + protected override bool IsUpdatable() + { + if (agent.Value != null) + { + return agent.Value.isStopped; + } + return false; + } + + protected override void OnAwake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/IsStopped.cs.meta b/Extend/NavmeshAgent/IsStopped.cs.meta new file mode 100644 index 0000000..94fd511 --- /dev/null +++ b/Extend/NavmeshAgent/IsStopped.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c47272e688124a843b72d484ad98adfd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/Move.cs b/Extend/NavmeshAgent/Move.cs new file mode 100644 index 0000000..8455fc2 --- /dev/null +++ b/Extend/NavmeshAgent/Move.cs @@ -0,0 +1,29 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: Move")] + [AkiInfo("Action: Apply relative movement to current position.")] + public class Move : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + [Tooltip(" The relative movement vector.")] + public SharedVector3 offset; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + agent.Value.Move(offset.Value); + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/Move.cs.meta b/Extend/NavmeshAgent/Move.cs.meta new file mode 100644 index 0000000..c9cd893 --- /dev/null +++ b/Extend/NavmeshAgent/Move.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ccfd8d673695e6d4fa51064c3eca4291 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/ResetPath.cs b/Extend/NavmeshAgent/ResetPath.cs new file mode 100644 index 0000000..8d00ada --- /dev/null +++ b/Extend/NavmeshAgent/ResetPath.cs @@ -0,0 +1,27 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: ResetPath")] + [AkiInfo("Action: Clears the current NavMeshAgent's path.")] + public class ResetPath : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + agent.Value.ResetPath(); + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/ResetPath.cs.meta b/Extend/NavmeshAgent/ResetPath.cs.meta new file mode 100644 index 0000000..4eebe59 --- /dev/null +++ b/Extend/NavmeshAgent/ResetPath.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2584bac07391be044af012193df746bc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/Resume.cs b/Extend/NavmeshAgent/Resume.cs new file mode 100644 index 0000000..cc0d7d5 --- /dev/null +++ b/Extend/NavmeshAgent/Resume.cs @@ -0,0 +1,27 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: Resume")] + [AkiInfo("Action: Resumes the movement along the current path after a pause.")] + public class Resume : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + agent.Value.isStopped = false; + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/Resume.cs.meta b/Extend/NavmeshAgent/Resume.cs.meta new file mode 100644 index 0000000..6ee8cbb --- /dev/null +++ b/Extend/NavmeshAgent/Resume.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4768f36812e77ab4ca6aa2498340c2da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/SetAcceleration.cs b/Extend/NavmeshAgent/SetAcceleration.cs new file mode 100644 index 0000000..65a19c3 --- /dev/null +++ b/Extend/NavmeshAgent/SetAcceleration.cs @@ -0,0 +1,28 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: SetAcceleration")] + [AkiInfo("Action: Set NavMeshAgent's maximum acceleration of an agent as it follows a path, given in units / sec^2.")] + public class SetAcceleration : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + public SharedFloat acceleration; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + agent.Value.acceleration = acceleration.Value; + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/SetAcceleration.cs.meta b/Extend/NavmeshAgent/SetAcceleration.cs.meta new file mode 100644 index 0000000..51483bd --- /dev/null +++ b/Extend/NavmeshAgent/SetAcceleration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 04fa49d5feab2924996cdc13bf4f2aab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/SetAngularSpeed.cs b/Extend/NavmeshAgent/SetAngularSpeed.cs new file mode 100644 index 0000000..8a245d6 --- /dev/null +++ b/Extend/NavmeshAgent/SetAngularSpeed.cs @@ -0,0 +1,28 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: SetAngularSpeed")] + [AkiInfo("Action: Set NavMeshAgent's maximum turning speed in (deg/s) while following a path.")] + public class SetAngularSpeed : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + public SharedFloat angularSpeed; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + agent.Value.angularSpeed = angularSpeed.Value; + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/SetAngularSpeed.cs.meta b/Extend/NavmeshAgent/SetAngularSpeed.cs.meta new file mode 100644 index 0000000..d5bd36d --- /dev/null +++ b/Extend/NavmeshAgent/SetAngularSpeed.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 63bdf96e0aa38364d9a4d047237da86f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/SetSpeed.cs b/Extend/NavmeshAgent/SetSpeed.cs new file mode 100644 index 0000000..272f89e --- /dev/null +++ b/Extend/NavmeshAgent/SetSpeed.cs @@ -0,0 +1,28 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: SetSpeed")] + [AkiInfo("Action: Set the maximum movement speed when following a path.")] + public class SetSpeed : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + public SharedFloat speed; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + agent.Value.speed = speed.Value; + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/SetSpeed.cs.meta b/Extend/NavmeshAgent/SetSpeed.cs.meta new file mode 100644 index 0000000..194a835 --- /dev/null +++ b/Extend/NavmeshAgent/SetSpeed.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 783d9f507091312469e50b56babab938 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/Stop.cs b/Extend/NavmeshAgent/Stop.cs new file mode 100644 index 0000000..a14b6d0 --- /dev/null +++ b/Extend/NavmeshAgent/Stop.cs @@ -0,0 +1,27 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: Stop")] + [AkiInfo("Action: Stop movement of this agent along its current path.")] + public class Stop : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + agent.Value.isStopped = true; + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/Stop.cs.meta b/Extend/NavmeshAgent/Stop.cs.meta new file mode 100644 index 0000000..4f9ff73 --- /dev/null +++ b/Extend/NavmeshAgent/Stop.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d50a582ea709a6240bdb4e4ad90210ab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/NavmeshAgent/Warp.cs b/Extend/NavmeshAgent/Warp.cs new file mode 100644 index 0000000..13a24bb --- /dev/null +++ b/Extend/NavmeshAgent/Warp.cs @@ -0,0 +1,29 @@ +using UnityEngine; +using UnityEngine.AI; +namespace Kurisu.AkiBT.Extend.UnityNavMeshAgent +{ + [AkiGroup("NavMeshAgent")] + [AkiLabel("NavMeshAgent: Warp")] + [AkiInfo("Action: Warps agent to the provided position.")] + public class Warp : Action + { + [Tooltip("If not filled in, it will be obtained from the bound gameObject")] + public SharedTObject agent; + [Tooltip("The position warp to")] + public SharedVector3 newPosition; + public override void Awake() + { + if (agent.Value == null) + agent.Value = GameObject.GetComponent(); + } + protected override Status OnUpdate() + { + if (agent.Value == null) + { + return Status.Failure; + } + agent.Value.Warp(newPosition.Value); + return Status.Success; + } + } +} \ No newline at end of file diff --git a/Extend/NavmeshAgent/Warp.cs.meta b/Extend/NavmeshAgent/Warp.cs.meta new file mode 100644 index 0000000..adc4747 --- /dev/null +++ b/Extend/NavmeshAgent/Warp.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 97e37cfbb7240d04688872d050b4d173 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Extend/Vector3/SetVector3.cs b/Extend/Vector3/SetVector3.cs new file mode 100644 index 0000000..7fb2c4b --- /dev/null +++ b/Extend/Vector3/SetVector3.cs @@ -0,0 +1,17 @@ +namespace Kurisu.AkiBT.Extend.Vector3 +{ + [AkiInfo("Action: Set Vector3 value")] + [AkiLabel("Vector3: SetVector3")] + [AkiGroup("Vector3")] + public class SetVector3 : Action + { + public SharedVector3 vector3; + [ForceShared] + public SharedVector3 storeResult; + protected override Status OnUpdate() + { + storeResult.Value = vector3.Value; + return Status.Success; + } + } +} diff --git a/Extend/Vector3/SetVector3.cs.meta b/Extend/Vector3/SetVector3.cs.meta new file mode 100644 index 0000000..2bddc27 --- /dev/null +++ b/Extend/Vector3/SetVector3.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7c1c25a4134408842a5098f9ed169ef7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md index 862ed99..ff53bc4 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@
-# AkiBT Version 1.4.8 +# AkiBT Version 1.5.0 ***Read this document in Chinese: [中文文档](./README_ZH.md)*** @@ -158,7 +158,7 @@ Different scopes can depend on each other. For example, ``SceneVariableScope`` c ## DSL -You can use [AkiBTDSL](https://github.com/AkiKurisu/AkiBTDSL) to make it easier to edit the behavior tree at runtime or outside the project. It can be exported to a DSL (domain specific language) format that is easy to read and modify, such as it is convenient to establish unified editing of Excel tables. +You can use [AkiBTDSL](./Docs/dsl.md) to make it easier to edit the behavior tree at runtime or outside the project. It can be exported to a DSL (domain specific language) format that is easy to read and modify, such as it is convenient to establish unified editing of Excel tables. ## User Service diff --git a/README_ZH.md b/README_ZH.md index e2278b3..c055780 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -2,7 +2,7 @@ [![Star on GitHub](https://img.shields.io/github/stars/AkiKurisu/AkiBT.svg)](https://github.com/AkiKurisu/AkiBT/stargazers)
-# 行为树 AkiBT Version 1.4.8 +# 行为树 AkiBT Version 1.5.0 ***Read this document in English: [English Document](./README.md)*** @@ -160,7 +160,7 @@ AkiBT是以[UniBT](https://github.com/yoshidan/UniBT)作为基础的行为树结 ## DSL -你可以使用[AkiBTDSL](https://github.com/AkiKurisu/AkiBTDSL)实现运行时或在项目外更方便编辑行为树, 可导出成便于阅读和修改的DSL(特定领域语言)格式,例如方便建立Excel表格统一编辑。 +你可以使用[AkiBTDSL](./Docs/dsl.md)实现运行时或在项目外更方便编辑行为树, 可导出成便于阅读和修改的DSL(特定领域语言)格式,例如方便建立Excel表格统一编辑。 ## 开发便捷服务 diff --git a/Runtime/AssemblyInfo.cs b/Runtime/Core/AssemblyInfo.cs similarity index 100% rename from Runtime/AssemblyInfo.cs rename to Runtime/Core/AssemblyInfo.cs diff --git a/Runtime/AssemblyInfo.cs.meta b/Runtime/Core/AssemblyInfo.cs.meta similarity index 100% rename from Runtime/AssemblyInfo.cs.meta rename to Runtime/Core/AssemblyInfo.cs.meta diff --git a/Runtime/Core/GameVariableScope.cs.meta b/Runtime/Core/GameVariableScope.cs.meta index d942eb0..ae1751f 100644 --- a/Runtime/Core/GameVariableScope.cs.meta +++ b/Runtime/Core/GameVariableScope.cs.meta @@ -5,7 +5,7 @@ MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 - icon: {instanceID: 0} + icon: {fileID: 2800000, guid: 3570ec2c8a2ccbf43878ea52e8e5e103, type: 3} userData: assetBundleName: assetBundleVariant: diff --git a/Runtime/Kurisu.AkiBT.asmdef b/Runtime/Core/Kurisu.AkiBT.asmdef similarity index 100% rename from Runtime/Kurisu.AkiBT.asmdef rename to Runtime/Core/Kurisu.AkiBT.asmdef diff --git a/Runtime/Kurisu.AkiBT.asmdef.meta b/Runtime/Core/Kurisu.AkiBT.asmdef.meta similarity index 100% rename from Runtime/Kurisu.AkiBT.asmdef.meta rename to Runtime/Core/Kurisu.AkiBT.asmdef.meta diff --git a/Runtime/Behavior.meta b/Runtime/Core/Model/Behavior.meta similarity index 100% rename from Runtime/Behavior.meta rename to Runtime/Core/Model/Behavior.meta diff --git a/Runtime/Behavior/Action.meta b/Runtime/Core/Model/Behavior/Action.meta similarity index 100% rename from Runtime/Behavior/Action.meta rename to Runtime/Core/Model/Behavior/Action.meta diff --git a/Runtime/Behavior/Action/Animator.meta b/Runtime/Core/Model/Behavior/Action/Animator.meta similarity index 100% rename from Runtime/Behavior/Action/Animator.meta rename to Runtime/Core/Model/Behavior/Action/Animator.meta diff --git a/Runtime/Behavior/Action/Animator/AnimatorAction.cs b/Runtime/Core/Model/Behavior/Action/Animator/AnimatorAction.cs similarity index 100% rename from Runtime/Behavior/Action/Animator/AnimatorAction.cs rename to Runtime/Core/Model/Behavior/Action/Animator/AnimatorAction.cs diff --git a/Runtime/Behavior/Action/Animator/AnimatorAction.cs.meta b/Runtime/Core/Model/Behavior/Action/Animator/AnimatorAction.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Animator/AnimatorAction.cs.meta rename to Runtime/Core/Model/Behavior/Action/Animator/AnimatorAction.cs.meta diff --git a/Runtime/Behavior/Action/Animator/AnimatorGetBool.cs b/Runtime/Core/Model/Behavior/Action/Animator/AnimatorGetBool.cs similarity index 100% rename from Runtime/Behavior/Action/Animator/AnimatorGetBool.cs rename to Runtime/Core/Model/Behavior/Action/Animator/AnimatorGetBool.cs diff --git a/Runtime/Behavior/Action/Animator/AnimatorGetBool.cs.meta b/Runtime/Core/Model/Behavior/Action/Animator/AnimatorGetBool.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Animator/AnimatorGetBool.cs.meta rename to Runtime/Core/Model/Behavior/Action/Animator/AnimatorGetBool.cs.meta diff --git a/Runtime/Behavior/Action/Animator/AnimatorSetBool.cs b/Runtime/Core/Model/Behavior/Action/Animator/AnimatorSetBool.cs similarity index 100% rename from Runtime/Behavior/Action/Animator/AnimatorSetBool.cs rename to Runtime/Core/Model/Behavior/Action/Animator/AnimatorSetBool.cs diff --git a/Runtime/Behavior/Action/Animator/AnimatorSetBool.cs.meta b/Runtime/Core/Model/Behavior/Action/Animator/AnimatorSetBool.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Animator/AnimatorSetBool.cs.meta rename to Runtime/Core/Model/Behavior/Action/Animator/AnimatorSetBool.cs.meta diff --git a/Runtime/Behavior/Action/Animator/AnimatorSetTrigger.cs b/Runtime/Core/Model/Behavior/Action/Animator/AnimatorSetTrigger.cs similarity index 100% rename from Runtime/Behavior/Action/Animator/AnimatorSetTrigger.cs rename to Runtime/Core/Model/Behavior/Action/Animator/AnimatorSetTrigger.cs diff --git a/Runtime/Behavior/Action/Animator/AnimatorSetTrigger.cs.meta b/Runtime/Core/Model/Behavior/Action/Animator/AnimatorSetTrigger.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Animator/AnimatorSetTrigger.cs.meta rename to Runtime/Core/Model/Behavior/Action/Animator/AnimatorSetTrigger.cs.meta diff --git a/Runtime/Behavior/Action/Animator/AnimatorWaitState.cs b/Runtime/Core/Model/Behavior/Action/Animator/AnimatorWaitState.cs similarity index 100% rename from Runtime/Behavior/Action/Animator/AnimatorWaitState.cs rename to Runtime/Core/Model/Behavior/Action/Animator/AnimatorWaitState.cs diff --git a/Runtime/Behavior/Action/Animator/AnimatorWaitState.cs.meta b/Runtime/Core/Model/Behavior/Action/Animator/AnimatorWaitState.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Animator/AnimatorWaitState.cs.meta rename to Runtime/Core/Model/Behavior/Action/Animator/AnimatorWaitState.cs.meta diff --git a/Runtime/Behavior/Action/Debug.meta b/Runtime/Core/Model/Behavior/Action/Debug.meta similarity index 100% rename from Runtime/Behavior/Action/Debug.meta rename to Runtime/Core/Model/Behavior/Action/Debug.meta diff --git a/Runtime/Behavior/Action/Debug/DebugLog.cs b/Runtime/Core/Model/Behavior/Action/Debug/DebugLog.cs similarity index 100% rename from Runtime/Behavior/Action/Debug/DebugLog.cs rename to Runtime/Core/Model/Behavior/Action/Debug/DebugLog.cs diff --git a/Runtime/Behavior/Action/Debug/DebugLog.cs.meta b/Runtime/Core/Model/Behavior/Action/Debug/DebugLog.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Debug/DebugLog.cs.meta rename to Runtime/Core/Model/Behavior/Action/Debug/DebugLog.cs.meta diff --git a/Runtime/Behavior/Action/Logic.meta b/Runtime/Core/Model/Behavior/Action/Logic.meta similarity index 100% rename from Runtime/Behavior/Action/Logic.meta rename to Runtime/Core/Model/Behavior/Action/Logic.meta diff --git a/Runtime/Behavior/Action/Logic/FixedReturn.cs b/Runtime/Core/Model/Behavior/Action/Logic/FixedReturn.cs similarity index 100% rename from Runtime/Behavior/Action/Logic/FixedReturn.cs rename to Runtime/Core/Model/Behavior/Action/Logic/FixedReturn.cs diff --git a/Runtime/Behavior/Action/Logic/FixedReturn.cs.meta b/Runtime/Core/Model/Behavior/Action/Logic/FixedReturn.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Logic/FixedReturn.cs.meta rename to Runtime/Core/Model/Behavior/Action/Logic/FixedReturn.cs.meta diff --git a/Runtime/Behavior/Action/Math.meta b/Runtime/Core/Model/Behavior/Action/Math.meta similarity index 100% rename from Runtime/Behavior/Action/Math.meta rename to Runtime/Core/Model/Behavior/Action/Math.meta diff --git a/Runtime/Behavior/Action/Math/BoolFlip.cs b/Runtime/Core/Model/Behavior/Action/Math/BoolFlip.cs similarity index 100% rename from Runtime/Behavior/Action/Math/BoolFlip.cs rename to Runtime/Core/Model/Behavior/Action/Math/BoolFlip.cs diff --git a/Runtime/Behavior/Action/Math/BoolFlip.cs.meta b/Runtime/Core/Model/Behavior/Action/Math/BoolFlip.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Math/BoolFlip.cs.meta rename to Runtime/Core/Model/Behavior/Action/Math/BoolFlip.cs.meta diff --git a/Runtime/Behavior/Action/Math/BoolOperator.cs b/Runtime/Core/Model/Behavior/Action/Math/BoolOperator.cs similarity index 100% rename from Runtime/Behavior/Action/Math/BoolOperator.cs rename to Runtime/Core/Model/Behavior/Action/Math/BoolOperator.cs diff --git a/Runtime/Behavior/Action/Math/BoolOperator.cs.meta b/Runtime/Core/Model/Behavior/Action/Math/BoolOperator.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Math/BoolOperator.cs.meta rename to Runtime/Core/Model/Behavior/Action/Math/BoolOperator.cs.meta diff --git a/Runtime/Behavior/Action/Math/Float2Int.cs b/Runtime/Core/Model/Behavior/Action/Math/Float2Int.cs similarity index 100% rename from Runtime/Behavior/Action/Math/Float2Int.cs rename to Runtime/Core/Model/Behavior/Action/Math/Float2Int.cs diff --git a/Runtime/Behavior/Action/Math/Float2Int.cs.meta b/Runtime/Core/Model/Behavior/Action/Math/Float2Int.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Math/Float2Int.cs.meta rename to Runtime/Core/Model/Behavior/Action/Math/Float2Int.cs.meta diff --git a/Runtime/Behavior/Action/Math/FloatOperator.cs b/Runtime/Core/Model/Behavior/Action/Math/FloatOperator.cs similarity index 100% rename from Runtime/Behavior/Action/Math/FloatOperator.cs rename to Runtime/Core/Model/Behavior/Action/Math/FloatOperator.cs diff --git a/Runtime/Behavior/Action/Math/FloatOperator.cs.meta b/Runtime/Core/Model/Behavior/Action/Math/FloatOperator.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Math/FloatOperator.cs.meta rename to Runtime/Core/Model/Behavior/Action/Math/FloatOperator.cs.meta diff --git a/Runtime/Behavior/Action/Math/Int2Float.cs b/Runtime/Core/Model/Behavior/Action/Math/Int2Float.cs similarity index 100% rename from Runtime/Behavior/Action/Math/Int2Float.cs rename to Runtime/Core/Model/Behavior/Action/Math/Int2Float.cs diff --git a/Runtime/Behavior/Action/Math/Int2Float.cs.meta b/Runtime/Core/Model/Behavior/Action/Math/Int2Float.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Math/Int2Float.cs.meta rename to Runtime/Core/Model/Behavior/Action/Math/Int2Float.cs.meta diff --git a/Runtime/Behavior/Action/Math/IntAbs.cs b/Runtime/Core/Model/Behavior/Action/Math/IntAbs.cs similarity index 100% rename from Runtime/Behavior/Action/Math/IntAbs.cs rename to Runtime/Core/Model/Behavior/Action/Math/IntAbs.cs diff --git a/Runtime/Behavior/Action/Math/IntAbs.cs.meta b/Runtime/Core/Model/Behavior/Action/Math/IntAbs.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Math/IntAbs.cs.meta rename to Runtime/Core/Model/Behavior/Action/Math/IntAbs.cs.meta diff --git a/Runtime/Behavior/Action/Math/IntOperator.cs b/Runtime/Core/Model/Behavior/Action/Math/IntOperator.cs similarity index 100% rename from Runtime/Behavior/Action/Math/IntOperator.cs rename to Runtime/Core/Model/Behavior/Action/Math/IntOperator.cs diff --git a/Runtime/Behavior/Action/Math/IntOperator.cs.meta b/Runtime/Core/Model/Behavior/Action/Math/IntOperator.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Math/IntOperator.cs.meta rename to Runtime/Core/Model/Behavior/Action/Math/IntOperator.cs.meta diff --git a/Runtime/Behavior/Action/Math/IntRandom.cs b/Runtime/Core/Model/Behavior/Action/Math/IntRandom.cs similarity index 100% rename from Runtime/Behavior/Action/Math/IntRandom.cs rename to Runtime/Core/Model/Behavior/Action/Math/IntRandom.cs diff --git a/Runtime/Behavior/Action/Math/IntRandom.cs.meta b/Runtime/Core/Model/Behavior/Action/Math/IntRandom.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Math/IntRandom.cs.meta rename to Runtime/Core/Model/Behavior/Action/Math/IntRandom.cs.meta diff --git a/Runtime/Behavior/Action/Math/SetBool.cs b/Runtime/Core/Model/Behavior/Action/Math/SetBool.cs similarity index 100% rename from Runtime/Behavior/Action/Math/SetBool.cs rename to Runtime/Core/Model/Behavior/Action/Math/SetBool.cs diff --git a/Runtime/Behavior/Action/Math/SetBool.cs.meta b/Runtime/Core/Model/Behavior/Action/Math/SetBool.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Math/SetBool.cs.meta rename to Runtime/Core/Model/Behavior/Action/Math/SetBool.cs.meta diff --git a/Runtime/Behavior/Action/Math/SetFloat.cs b/Runtime/Core/Model/Behavior/Action/Math/SetFloat.cs similarity index 100% rename from Runtime/Behavior/Action/Math/SetFloat.cs rename to Runtime/Core/Model/Behavior/Action/Math/SetFloat.cs diff --git a/Runtime/Behavior/Action/Math/SetFloat.cs.meta b/Runtime/Core/Model/Behavior/Action/Math/SetFloat.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Math/SetFloat.cs.meta rename to Runtime/Core/Model/Behavior/Action/Math/SetFloat.cs.meta diff --git a/Runtime/Behavior/Action/Math/SetInt.cs b/Runtime/Core/Model/Behavior/Action/Math/SetInt.cs similarity index 100% rename from Runtime/Behavior/Action/Math/SetInt.cs rename to Runtime/Core/Model/Behavior/Action/Math/SetInt.cs diff --git a/Runtime/Behavior/Action/Math/SetInt.cs.meta b/Runtime/Core/Model/Behavior/Action/Math/SetInt.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Math/SetInt.cs.meta rename to Runtime/Core/Model/Behavior/Action/Math/SetInt.cs.meta diff --git a/Runtime/Behavior/Action/Navmesh.meta b/Runtime/Core/Model/Behavior/Action/NavMeshAgent.meta similarity index 100% rename from Runtime/Behavior/Action/Navmesh.meta rename to Runtime/Core/Model/Behavior/Action/NavMeshAgent.meta diff --git a/Runtime/Behavior/Action/Navmesh/NavmeshSetDestination.cs b/Runtime/Core/Model/Behavior/Action/NavMeshAgent/SetDestination.cs similarity index 68% rename from Runtime/Behavior/Action/Navmesh/NavmeshSetDestination.cs rename to Runtime/Core/Model/Behavior/Action/NavMeshAgent/SetDestination.cs index 72e8dc8..23c0fda 100644 --- a/Runtime/Behavior/Action/Navmesh/NavmeshSetDestination.cs +++ b/Runtime/Core/Model/Behavior/Action/NavMeshAgent/SetDestination.cs @@ -2,9 +2,9 @@ using UnityEngine.AI; namespace Kurisu.AkiBT.Extend { - [AkiInfo("Action: Set the destination of NavmeshAgent")] - [AkiLabel("Navmesh: SetDestination")] - [AkiGroup("Navmesh")] + [AkiInfo("Action: Set the destination of NavMeshAgent")] + [AkiLabel("NavMeshAgent: SetDestination")] + [AkiGroup("NavMeshAgent")] public class NavmeshSetDestination : Action { [Tooltip("If not filled in, it will be obtained from the bound gameObject")] @@ -12,10 +12,11 @@ public class NavmeshSetDestination : Action public SharedVector3 destination; protected override Status OnUpdate() { - if (agent != null) + if (agent.Value == null) { - agent.Value.destination = destination.Value; + return Status.Failure; } + agent.Value.destination = destination.Value; return Status.Success; } public override void Awake() diff --git a/Runtime/Behavior/Action/Navmesh/NavmeshSetDestination.cs.meta b/Runtime/Core/Model/Behavior/Action/NavMeshAgent/SetDestination.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Navmesh/NavmeshSetDestination.cs.meta rename to Runtime/Core/Model/Behavior/Action/NavMeshAgent/SetDestination.cs.meta diff --git a/Runtime/Behavior/Action/Navmesh/NavmeshStopAgent.cs b/Runtime/Core/Model/Behavior/Action/NavMeshAgent/Stop.cs similarity index 84% rename from Runtime/Behavior/Action/Navmesh/NavmeshStopAgent.cs rename to Runtime/Core/Model/Behavior/Action/NavMeshAgent/Stop.cs index ddc1f53..26f0163 100644 --- a/Runtime/Behavior/Action/Navmesh/NavmeshStopAgent.cs +++ b/Runtime/Core/Model/Behavior/Action/NavMeshAgent/Stop.cs @@ -2,9 +2,9 @@ using UnityEngine.AI; namespace Kurisu.AkiBT.Extend { - [AkiInfo("Action: Stop NavmeshAgent according to isStopped")] - [AkiLabel("Navmesh: StopAgent")] - [AkiGroup("Navmesh")] + [AkiInfo("Action: Stop NavMeshAgent according to isStopped")] + [AkiLabel("NavMeshAgent: StopAgent")] + [AkiGroup("NavMeshAgent")] public class NavmeshStopAgent : Action { [Tooltip("If not filled in, it will be obtained from the bound gameObject")] diff --git a/Runtime/Behavior/Action/Navmesh/NavmeshStopAgent.cs.meta b/Runtime/Core/Model/Behavior/Action/NavMeshAgent/Stop.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Navmesh/NavmeshStopAgent.cs.meta rename to Runtime/Core/Model/Behavior/Action/NavMeshAgent/Stop.cs.meta diff --git a/Runtime/Behavior/Action/String.meta b/Runtime/Core/Model/Behavior/Action/String.meta similarity index 100% rename from Runtime/Behavior/Action/String.meta rename to Runtime/Core/Model/Behavior/Action/String.meta diff --git a/Runtime/Behavior/Action/String/BuildString.cs b/Runtime/Core/Model/Behavior/Action/String/BuildString.cs similarity index 100% rename from Runtime/Behavior/Action/String/BuildString.cs rename to Runtime/Core/Model/Behavior/Action/String/BuildString.cs diff --git a/Runtime/Behavior/Action/String/BuildString.cs.meta b/Runtime/Core/Model/Behavior/Action/String/BuildString.cs.meta similarity index 100% rename from Runtime/Behavior/Action/String/BuildString.cs.meta rename to Runtime/Core/Model/Behavior/Action/String/BuildString.cs.meta diff --git a/Runtime/Behavior/Action/String/FormatString.cs b/Runtime/Core/Model/Behavior/Action/String/FormatString.cs similarity index 100% rename from Runtime/Behavior/Action/String/FormatString.cs rename to Runtime/Core/Model/Behavior/Action/String/FormatString.cs diff --git a/Runtime/Behavior/Action/String/FormatString.cs.meta b/Runtime/Core/Model/Behavior/Action/String/FormatString.cs.meta similarity index 100% rename from Runtime/Behavior/Action/String/FormatString.cs.meta rename to Runtime/Core/Model/Behavior/Action/String/FormatString.cs.meta diff --git a/Runtime/Behavior/Action/String/ReplaceString.cs b/Runtime/Core/Model/Behavior/Action/String/ReplaceString.cs similarity index 100% rename from Runtime/Behavior/Action/String/ReplaceString.cs rename to Runtime/Core/Model/Behavior/Action/String/ReplaceString.cs diff --git a/Runtime/Behavior/Action/String/ReplaceString.cs.meta b/Runtime/Core/Model/Behavior/Action/String/ReplaceString.cs.meta similarity index 100% rename from Runtime/Behavior/Action/String/ReplaceString.cs.meta rename to Runtime/Core/Model/Behavior/Action/String/ReplaceString.cs.meta diff --git a/Runtime/Behavior/Action/String/SetString.cs b/Runtime/Core/Model/Behavior/Action/String/SetString.cs similarity index 100% rename from Runtime/Behavior/Action/String/SetString.cs rename to Runtime/Core/Model/Behavior/Action/String/SetString.cs diff --git a/Runtime/Behavior/Action/String/SetString.cs.meta b/Runtime/Core/Model/Behavior/Action/String/SetString.cs.meta similarity index 100% rename from Runtime/Behavior/Action/String/SetString.cs.meta rename to Runtime/Core/Model/Behavior/Action/String/SetString.cs.meta diff --git a/Runtime/Behavior/Action/String/StringRandom.cs b/Runtime/Core/Model/Behavior/Action/String/StringRandom.cs similarity index 100% rename from Runtime/Behavior/Action/String/StringRandom.cs rename to Runtime/Core/Model/Behavior/Action/String/StringRandom.cs diff --git a/Runtime/Behavior/Action/String/StringRandom.cs.meta b/Runtime/Core/Model/Behavior/Action/String/StringRandom.cs.meta similarity index 100% rename from Runtime/Behavior/Action/String/StringRandom.cs.meta rename to Runtime/Core/Model/Behavior/Action/String/StringRandom.cs.meta diff --git a/Runtime/Behavior/Action/Time.meta b/Runtime/Core/Model/Behavior/Action/Time.meta similarity index 100% rename from Runtime/Behavior/Action/Time.meta rename to Runtime/Core/Model/Behavior/Action/Time.meta diff --git a/Runtime/Behavior/Action/Time/TimeWait.cs b/Runtime/Core/Model/Behavior/Action/Time/TimeWait.cs similarity index 100% rename from Runtime/Behavior/Action/Time/TimeWait.cs rename to Runtime/Core/Model/Behavior/Action/Time/TimeWait.cs diff --git a/Runtime/Behavior/Action/Time/TimeWait.cs.meta b/Runtime/Core/Model/Behavior/Action/Time/TimeWait.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Time/TimeWait.cs.meta rename to Runtime/Core/Model/Behavior/Action/Time/TimeWait.cs.meta diff --git a/Runtime/Behavior/Action/Transform.meta b/Runtime/Core/Model/Behavior/Action/Transform.meta similarity index 100% rename from Runtime/Behavior/Action/Transform.meta rename to Runtime/Core/Model/Behavior/Action/Transform.meta diff --git a/Runtime/Behavior/Action/Transform/TransformGetPosition.cs b/Runtime/Core/Model/Behavior/Action/Transform/TransformGetPosition.cs similarity index 100% rename from Runtime/Behavior/Action/Transform/TransformGetPosition.cs rename to Runtime/Core/Model/Behavior/Action/Transform/TransformGetPosition.cs diff --git a/Runtime/Behavior/Action/Transform/TransformGetPosition.cs.meta b/Runtime/Core/Model/Behavior/Action/Transform/TransformGetPosition.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Transform/TransformGetPosition.cs.meta rename to Runtime/Core/Model/Behavior/Action/Transform/TransformGetPosition.cs.meta diff --git a/Runtime/Behavior/Action/UnityEvent.meta b/Runtime/Core/Model/Behavior/Action/UnityEvent.meta similarity index 100% rename from Runtime/Behavior/Action/UnityEvent.meta rename to Runtime/Core/Model/Behavior/Action/UnityEvent.meta diff --git a/Runtime/Behavior/Action/UnityEvent/InvokeUnityEvent.cs b/Runtime/Core/Model/Behavior/Action/UnityEvent/InvokeUnityEvent.cs similarity index 100% rename from Runtime/Behavior/Action/UnityEvent/InvokeUnityEvent.cs rename to Runtime/Core/Model/Behavior/Action/UnityEvent/InvokeUnityEvent.cs diff --git a/Runtime/Behavior/Action/UnityEvent/InvokeUnityEvent.cs.meta b/Runtime/Core/Model/Behavior/Action/UnityEvent/InvokeUnityEvent.cs.meta similarity index 100% rename from Runtime/Behavior/Action/UnityEvent/InvokeUnityEvent.cs.meta rename to Runtime/Core/Model/Behavior/Action/UnityEvent/InvokeUnityEvent.cs.meta diff --git a/Runtime/Behavior/Action/Vector3.meta b/Runtime/Core/Model/Behavior/Action/Vector3.meta similarity index 100% rename from Runtime/Behavior/Action/Vector3.meta rename to Runtime/Core/Model/Behavior/Action/Vector3.meta diff --git a/Runtime/Behavior/Action/Vector3/Vector3Distance.cs b/Runtime/Core/Model/Behavior/Action/Vector3/Vector3Distance.cs similarity index 100% rename from Runtime/Behavior/Action/Vector3/Vector3Distance.cs rename to Runtime/Core/Model/Behavior/Action/Vector3/Vector3Distance.cs diff --git a/Runtime/Behavior/Action/Vector3/Vector3Distance.cs.meta b/Runtime/Core/Model/Behavior/Action/Vector3/Vector3Distance.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Vector3/Vector3Distance.cs.meta rename to Runtime/Core/Model/Behavior/Action/Vector3/Vector3Distance.cs.meta diff --git a/Runtime/Behavior/Action/Vector3/Vector3GetSqrMagnitude.cs b/Runtime/Core/Model/Behavior/Action/Vector3/Vector3GetSqrMagnitude.cs similarity index 100% rename from Runtime/Behavior/Action/Vector3/Vector3GetSqrMagnitude.cs rename to Runtime/Core/Model/Behavior/Action/Vector3/Vector3GetSqrMagnitude.cs diff --git a/Runtime/Behavior/Action/Vector3/Vector3GetSqrMagnitude.cs.meta b/Runtime/Core/Model/Behavior/Action/Vector3/Vector3GetSqrMagnitude.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Vector3/Vector3GetSqrMagnitude.cs.meta rename to Runtime/Core/Model/Behavior/Action/Vector3/Vector3GetSqrMagnitude.cs.meta diff --git a/Runtime/Behavior/Action/Vector3/Vector3Operator.cs b/Runtime/Core/Model/Behavior/Action/Vector3/Vector3Operator.cs similarity index 100% rename from Runtime/Behavior/Action/Vector3/Vector3Operator.cs rename to Runtime/Core/Model/Behavior/Action/Vector3/Vector3Operator.cs diff --git a/Runtime/Behavior/Action/Vector3/Vector3Operator.cs.meta b/Runtime/Core/Model/Behavior/Action/Vector3/Vector3Operator.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Vector3/Vector3Operator.cs.meta rename to Runtime/Core/Model/Behavior/Action/Vector3/Vector3Operator.cs.meta diff --git a/Runtime/Behavior/Action/Vector3/Vector3Random.cs b/Runtime/Core/Model/Behavior/Action/Vector3/Vector3Random.cs similarity index 100% rename from Runtime/Behavior/Action/Vector3/Vector3Random.cs rename to Runtime/Core/Model/Behavior/Action/Vector3/Vector3Random.cs diff --git a/Runtime/Behavior/Action/Vector3/Vector3Random.cs.meta b/Runtime/Core/Model/Behavior/Action/Vector3/Vector3Random.cs.meta similarity index 100% rename from Runtime/Behavior/Action/Vector3/Vector3Random.cs.meta rename to Runtime/Core/Model/Behavior/Action/Vector3/Vector3Random.cs.meta diff --git a/Runtime/Behavior/Composite.meta b/Runtime/Core/Model/Behavior/Composite.meta similarity index 100% rename from Runtime/Behavior/Composite.meta rename to Runtime/Core/Model/Behavior/Composite.meta diff --git a/Runtime/Behavior/Composite/Parallel.cs b/Runtime/Core/Model/Behavior/Composite/Parallel.cs similarity index 100% rename from Runtime/Behavior/Composite/Parallel.cs rename to Runtime/Core/Model/Behavior/Composite/Parallel.cs diff --git a/Runtime/Behavior/Composite/Parallel.cs.meta b/Runtime/Core/Model/Behavior/Composite/Parallel.cs.meta similarity index 100% rename from Runtime/Behavior/Composite/Parallel.cs.meta rename to Runtime/Core/Model/Behavior/Composite/Parallel.cs.meta diff --git a/Runtime/Behavior/Composite/Random.cs b/Runtime/Core/Model/Behavior/Composite/Random.cs similarity index 100% rename from Runtime/Behavior/Composite/Random.cs rename to Runtime/Core/Model/Behavior/Composite/Random.cs diff --git a/Runtime/Behavior/Composite/Random.cs.meta b/Runtime/Core/Model/Behavior/Composite/Random.cs.meta similarity index 100% rename from Runtime/Behavior/Composite/Random.cs.meta rename to Runtime/Core/Model/Behavior/Composite/Random.cs.meta diff --git a/Runtime/Behavior/Composite/Rotator.cs b/Runtime/Core/Model/Behavior/Composite/Rotator.cs similarity index 100% rename from Runtime/Behavior/Composite/Rotator.cs rename to Runtime/Core/Model/Behavior/Composite/Rotator.cs diff --git a/Runtime/Behavior/Composite/Rotator.cs.meta b/Runtime/Core/Model/Behavior/Composite/Rotator.cs.meta similarity index 100% rename from Runtime/Behavior/Composite/Rotator.cs.meta rename to Runtime/Core/Model/Behavior/Composite/Rotator.cs.meta diff --git a/Runtime/Behavior/Composite/Selector.cs b/Runtime/Core/Model/Behavior/Composite/Selector.cs similarity index 100% rename from Runtime/Behavior/Composite/Selector.cs rename to Runtime/Core/Model/Behavior/Composite/Selector.cs diff --git a/Runtime/Behavior/Composite/Selector.cs.meta b/Runtime/Core/Model/Behavior/Composite/Selector.cs.meta similarity index 100% rename from Runtime/Behavior/Composite/Selector.cs.meta rename to Runtime/Core/Model/Behavior/Composite/Selector.cs.meta diff --git a/Runtime/Behavior/Composite/Sequence.cs b/Runtime/Core/Model/Behavior/Composite/Sequence.cs similarity index 100% rename from Runtime/Behavior/Composite/Sequence.cs rename to Runtime/Core/Model/Behavior/Composite/Sequence.cs diff --git a/Runtime/Behavior/Composite/Sequence.cs.meta b/Runtime/Core/Model/Behavior/Composite/Sequence.cs.meta similarity index 100% rename from Runtime/Behavior/Composite/Sequence.cs.meta rename to Runtime/Core/Model/Behavior/Composite/Sequence.cs.meta diff --git a/Runtime/Behavior/Composite/WeightedRandom.cs b/Runtime/Core/Model/Behavior/Composite/WeightedRandom.cs similarity index 75% rename from Runtime/Behavior/Composite/WeightedRandom.cs rename to Runtime/Core/Model/Behavior/Composite/WeightedRandom.cs index d0e7bc5..4bcfa2c 100644 --- a/Runtime/Behavior/Composite/WeightedRandom.cs +++ b/Runtime/Core/Model/Behavior/Composite/WeightedRandom.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using UnityEngine; namespace Kurisu.AkiBT { @@ -7,9 +6,9 @@ namespace Kurisu.AkiBT public class WeightedRandom : Composite { private NodeBehavior runningNode; - [Tooltip("Node weight list, when the length of the list is greater than the number of child nodes" + - ", the excess part will not be included in the weight")] - public List weights = new(); + [Tooltip("Children weights, when weights length is greater than children count" + + ", the excess part will be ignored")] + public float[] weights; protected override Status OnUpdate() { // update running node if previous status is Running. @@ -30,7 +29,7 @@ private Status HandleStatus(Status status, NodeBehavior updated) private int GetNext() { float total = 0; - int count = Mathf.Min(weights.Count, Children.Count); + int count = Mathf.Min(weights.Length, Children.Count); for (int i = 0; i < count; i++) { total += weights[i]; @@ -47,11 +46,8 @@ private int GetNext() public override void Abort() { - if (runningNode != null) - { - runningNode.Abort(); - runningNode = null; - } + runningNode?.Abort(); + runningNode = null; } } } \ No newline at end of file diff --git a/Runtime/Behavior/Composite/WeightedRandom.cs.meta b/Runtime/Core/Model/Behavior/Composite/WeightedRandom.cs.meta similarity index 100% rename from Runtime/Behavior/Composite/WeightedRandom.cs.meta rename to Runtime/Core/Model/Behavior/Composite/WeightedRandom.cs.meta diff --git a/Runtime/Behavior/Condition.meta b/Runtime/Core/Model/Behavior/Condition.meta similarity index 100% rename from Runtime/Behavior/Condition.meta rename to Runtime/Core/Model/Behavior/Condition.meta diff --git a/Runtime/Behavior/Condition/Animator.meta b/Runtime/Core/Model/Behavior/Condition/Animator.meta similarity index 100% rename from Runtime/Behavior/Condition/Animator.meta rename to Runtime/Core/Model/Behavior/Condition/Animator.meta diff --git a/Runtime/Behavior/Condition/Animator/AnimatorBoolCondition.cs b/Runtime/Core/Model/Behavior/Condition/Animator/AnimatorBoolCondition.cs similarity index 100% rename from Runtime/Behavior/Condition/Animator/AnimatorBoolCondition.cs rename to Runtime/Core/Model/Behavior/Condition/Animator/AnimatorBoolCondition.cs diff --git a/Runtime/Behavior/Condition/Animator/AnimatorBoolCondition.cs.meta b/Runtime/Core/Model/Behavior/Condition/Animator/AnimatorBoolCondition.cs.meta similarity index 100% rename from Runtime/Behavior/Condition/Animator/AnimatorBoolCondition.cs.meta rename to Runtime/Core/Model/Behavior/Condition/Animator/AnimatorBoolCondition.cs.meta diff --git a/Runtime/Behavior/Condition/Animator/AnimatorCondition.cs b/Runtime/Core/Model/Behavior/Condition/Animator/AnimatorCondition.cs similarity index 100% rename from Runtime/Behavior/Condition/Animator/AnimatorCondition.cs rename to Runtime/Core/Model/Behavior/Condition/Animator/AnimatorCondition.cs diff --git a/Runtime/Behavior/Condition/Animator/AnimatorCondition.cs.meta b/Runtime/Core/Model/Behavior/Condition/Animator/AnimatorCondition.cs.meta similarity index 100% rename from Runtime/Behavior/Condition/Animator/AnimatorCondition.cs.meta rename to Runtime/Core/Model/Behavior/Condition/Animator/AnimatorCondition.cs.meta diff --git a/Runtime/Behavior/Condition/Input.meta b/Runtime/Core/Model/Behavior/Condition/Input.meta similarity index 100% rename from Runtime/Behavior/Condition/Input.meta rename to Runtime/Core/Model/Behavior/Condition/Input.meta diff --git a/Runtime/Behavior/Condition/Input/InputGetKeyDownCondition.cs b/Runtime/Core/Model/Behavior/Condition/Input/InputGetKeyDownCondition.cs similarity index 100% rename from Runtime/Behavior/Condition/Input/InputGetKeyDownCondition.cs rename to Runtime/Core/Model/Behavior/Condition/Input/InputGetKeyDownCondition.cs diff --git a/Runtime/Behavior/Condition/Input/InputGetKeyDownCondition.cs.meta b/Runtime/Core/Model/Behavior/Condition/Input/InputGetKeyDownCondition.cs.meta similarity index 100% rename from Runtime/Behavior/Condition/Input/InputGetKeyDownCondition.cs.meta rename to Runtime/Core/Model/Behavior/Condition/Input/InputGetKeyDownCondition.cs.meta diff --git a/Runtime/Behavior/Condition/Math.meta b/Runtime/Core/Model/Behavior/Condition/Math.meta similarity index 100% rename from Runtime/Behavior/Condition/Math.meta rename to Runtime/Core/Model/Behavior/Condition/Math.meta diff --git a/Runtime/Behavior/Condition/Math/BoolComparison.cs b/Runtime/Core/Model/Behavior/Condition/Math/BoolComparison.cs similarity index 100% rename from Runtime/Behavior/Condition/Math/BoolComparison.cs rename to Runtime/Core/Model/Behavior/Condition/Math/BoolComparison.cs diff --git a/Runtime/Behavior/Condition/Math/BoolComparison.cs.meta b/Runtime/Core/Model/Behavior/Condition/Math/BoolComparison.cs.meta similarity index 100% rename from Runtime/Behavior/Condition/Math/BoolComparison.cs.meta rename to Runtime/Core/Model/Behavior/Condition/Math/BoolComparison.cs.meta diff --git a/Runtime/Behavior/Condition/Math/FloatComparison.cs b/Runtime/Core/Model/Behavior/Condition/Math/FloatComparison.cs similarity index 100% rename from Runtime/Behavior/Condition/Math/FloatComparison.cs rename to Runtime/Core/Model/Behavior/Condition/Math/FloatComparison.cs diff --git a/Runtime/Behavior/Condition/Math/FloatComparison.cs.meta b/Runtime/Core/Model/Behavior/Condition/Math/FloatComparison.cs.meta similarity index 100% rename from Runtime/Behavior/Condition/Math/FloatComparison.cs.meta rename to Runtime/Core/Model/Behavior/Condition/Math/FloatComparison.cs.meta diff --git a/Runtime/Behavior/Condition/Math/IntComparison.cs b/Runtime/Core/Model/Behavior/Condition/Math/IntComparison.cs similarity index 100% rename from Runtime/Behavior/Condition/Math/IntComparison.cs rename to Runtime/Core/Model/Behavior/Condition/Math/IntComparison.cs diff --git a/Runtime/Behavior/Condition/Math/IntComparison.cs.meta b/Runtime/Core/Model/Behavior/Condition/Math/IntComparison.cs.meta similarity index 100% rename from Runtime/Behavior/Condition/Math/IntComparison.cs.meta rename to Runtime/Core/Model/Behavior/Condition/Math/IntComparison.cs.meta diff --git a/Runtime/Behavior/Decorator.meta b/Runtime/Core/Model/Behavior/Decorator.meta similarity index 100% rename from Runtime/Behavior/Decorator.meta rename to Runtime/Core/Model/Behavior/Decorator.meta diff --git a/Runtime/Behavior/Decorator/Invertor.cs b/Runtime/Core/Model/Behavior/Decorator/Invertor.cs similarity index 100% rename from Runtime/Behavior/Decorator/Invertor.cs rename to Runtime/Core/Model/Behavior/Decorator/Invertor.cs diff --git a/Runtime/Behavior/Decorator/Invertor.cs.meta b/Runtime/Core/Model/Behavior/Decorator/Invertor.cs.meta similarity index 100% rename from Runtime/Behavior/Decorator/Invertor.cs.meta rename to Runtime/Core/Model/Behavior/Decorator/Invertor.cs.meta diff --git a/Runtime/Behavior/Decorator/Repeater.cs b/Runtime/Core/Model/Behavior/Decorator/Repeater.cs similarity index 100% rename from Runtime/Behavior/Decorator/Repeater.cs rename to Runtime/Core/Model/Behavior/Decorator/Repeater.cs diff --git a/Runtime/Behavior/Decorator/Repeater.cs.meta b/Runtime/Core/Model/Behavior/Decorator/Repeater.cs.meta similarity index 100% rename from Runtime/Behavior/Decorator/Repeater.cs.meta rename to Runtime/Core/Model/Behavior/Decorator/Repeater.cs.meta diff --git a/Runtime/Behavior/Decorator/ReturnSuccess.cs b/Runtime/Core/Model/Behavior/Decorator/ReturnSuccess.cs similarity index 100% rename from Runtime/Behavior/Decorator/ReturnSuccess.cs rename to Runtime/Core/Model/Behavior/Decorator/ReturnSuccess.cs diff --git a/Runtime/Behavior/Decorator/ReturnSuccess.cs.meta b/Runtime/Core/Model/Behavior/Decorator/ReturnSuccess.cs.meta similarity index 100% rename from Runtime/Behavior/Decorator/ReturnSuccess.cs.meta rename to Runtime/Core/Model/Behavior/Decorator/ReturnSuccess.cs.meta diff --git a/Runtime/Behavior/Decorator/WaitSuccess.cs b/Runtime/Core/Model/Behavior/Decorator/WaitSuccess.cs similarity index 100% rename from Runtime/Behavior/Decorator/WaitSuccess.cs rename to Runtime/Core/Model/Behavior/Decorator/WaitSuccess.cs diff --git a/Runtime/Behavior/Decorator/WaitSuccess.cs.meta b/Runtime/Core/Model/Behavior/Decorator/WaitSuccess.cs.meta similarity index 100% rename from Runtime/Behavior/Decorator/WaitSuccess.cs.meta rename to Runtime/Core/Model/Behavior/Decorator/WaitSuccess.cs.meta diff --git a/Runtime/Core/Model/BehaviorTree.cs b/Runtime/Core/Model/BehaviorTree.cs index 6a1b49e..216b35d 100644 --- a/Runtime/Core/Model/BehaviorTree.cs +++ b/Runtime/Core/Model/BehaviorTree.cs @@ -4,6 +4,8 @@ using System.Collections; using System; using UnityEngine.Pool; +using System.Reflection; +using UnityEngine.Assertions; namespace Kurisu.AkiBT { [Serializable] @@ -20,7 +22,8 @@ public class BehaviorTree : IEnumerable, IDisposable #endif // Exposed blackboard for data exchange public BlackBoard BlackBoard { get; private set; } - internal readonly HashSet internalVariables = new(); + private readonly HashSet internalVariables = new(); + private static readonly Dictionary> fieldInfoLookup = new(); public BehaviorTree() { } public BehaviorTree(BehaviorTreeData behaviorTreeData) { @@ -38,7 +41,7 @@ public BehaviorTree(BehaviorTreeData behaviorTreeData) public void InitVariables() { BlackBoard ??= BlackBoard.Create(variables, false); - SharedVariableHelper.InitVariables(this); + InitVariables_Imp(this); } public void Run(GameObject gameObject) { @@ -98,6 +101,10 @@ IEnumerator IEnumerable.GetEnumerator() /// public BehaviorTreeData GetData() { +#if UNITY_EDITOR + // Should not serialize data in playing mode which will modify behavior tree structure + Assert.IsFalse(Application.isPlaying); +#endif // since this function used in editor most of time // use clone to prevent modify source tree return new BehaviorTreeData(this).Clone(); @@ -116,6 +123,70 @@ public static string Serialize(BehaviorTree tree, bool indented = false, bool se if (tree == null) return null; return BehaviorTreeData.Serialize(tree.GetData(), indented, serializeEditorData); } + /// + /// Traverse the behavior tree and automatically init all shared variables + /// + /// API Updater: Update node {pair.sourceType.nodeType} to {pair.targetType.nodeType}"); + behaviors[childIndex] = child = (NodeBehavior)SmartDeserialize(nodeData[childIndex].serializedData, pair.targetType.Type); + } + } +#endif // use invalid node to replace missing nodes if (child == null) { @@ -121,33 +133,17 @@ public BehaviorTree CreateInstance() return new BehaviorTree(Clone()); } /// - /// Deserialize from json, but has limit at runtime + /// Deserialize from json /// /// /// public static BehaviorTreeData Deserialize(string serializedData) { #if UNITY_EDITOR - if (!string.IsNullOrEmpty(serializedData)) - { - JObject obj = JObject.Parse(serializedData); - foreach (JProperty prop in obj.Descendants().OfType().ToList()) - { - if (prop.Name == "instanceID") - { - var UObject = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath((string)prop.Value)); - if (UObject == null) - { - prop.Value = 0; - continue; - } - prop.Value = UObject.GetInstanceID(); - } - } - return JsonUtility.FromJson(obj.ToString(Formatting.Indented)); - } -#endif + return SmartDeserialize(serializedData, typeof(BehaviorTreeData)) as BehaviorTreeData; +#else return JsonUtility.FromJson(serializedData); +#endif } /// /// Serialize to json @@ -162,13 +158,14 @@ public static string Serialize(BehaviorTreeData tree, bool indented = false, boo return SmartSerialize(tree, indented, serializeEditorData); } /// - /// Format json smarter in editor + /// Serialize json smarter in editor /// /// /// /// + /// /// - internal static string SmartSerialize(object data, bool indented = false, bool serializeEditorData = false) + internal static string SmartSerialize(object data, bool indented = false, bool serializeEditorData = false, bool verbose = true) { string json = JsonUtility.ToJson(data, indented); #if UNITY_EDITOR @@ -192,12 +189,13 @@ internal static string SmartSerialize(object data, bool indented = false, bool s string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(UObject)); if (string.IsNullOrEmpty(guid)) { - // if reference objects inside prefab or in scene - Debug.LogWarning($"AkiBT: Can't serialize UnityEngine.Object field {propertyName}"); + // TODO: if reference objects inside prefab, may use its relative path + if (verbose) + Debug.LogWarning($"AkiBT: Can't serialize {propertyName} {UObject} in a Scene."); continue; } //Convert to GUID - prop.Value = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(UObject)); + prop.Value = guid; } } return obj.ToString(indented ? Formatting.Indented : Formatting.None); @@ -205,5 +203,29 @@ internal static string SmartSerialize(object data, bool indented = false, bool s return json; #endif } + internal static object SmartDeserialize(string serializedData, Type type) + { +#if UNITY_EDITOR + if (!string.IsNullOrEmpty(serializedData)) + { + JObject obj = JObject.Parse(serializedData); + foreach (JProperty prop in obj.Descendants().OfType().ToList()) + { + if (prop.Name == "instanceID") + { + var UObject = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath((string)prop.Value)); + if (UObject == null) + { + prop.Value = 0; + continue; + } + prop.Value = UObject.GetInstanceID(); + } + } + return JsonUtility.FromJson(obj.ToString(Formatting.Indented), type); + } +#endif + return JsonUtility.FromJson(serializedData, type); + } } } \ No newline at end of file diff --git a/Runtime/Core/Model/Node/Composite.cs b/Runtime/Core/Model/Node/Composite.cs index 090786a..f8f6a1f 100644 --- a/Runtime/Core/Model/Node/Composite.cs +++ b/Runtime/Core/Model/Node/Composite.cs @@ -50,11 +50,6 @@ public sealed override void PostUpdate() foreach (var child in children) child.PostUpdate(); } - - /// - /// 组合结点可以增加子结点 - /// - /// public sealed override void AddChild(NodeBehavior child) { children.Add(child); @@ -72,6 +67,15 @@ public sealed override void ClearChildren() { children.Clear(); } + public sealed override void SetChildren(NodeBehavior[] inChildren) + { + children.Clear(); + children.AddRange(inChildren); + } + public sealed override NodeBehavior[] GetChildren() + { + return children.ToArray(); + } public sealed override void Dispose() { base.Dispose(); diff --git a/Runtime/Core/Model/Node/Conditional.cs b/Runtime/Core/Model/Node/Conditional.cs index 4e10b1a..670923e 100644 --- a/Runtime/Core/Model/Node/Conditional.cs +++ b/Runtime/Core/Model/Node/Conditional.cs @@ -126,5 +126,14 @@ public sealed override void AddChild(NodeBehavior nodeBehavior) { child = nodeBehavior; } + public sealed override void SetChildren(NodeBehavior[] inChildren) + { + child = inChildren[0]; + } + public sealed override NodeBehavior[] GetChildren() + { + if (child == null) return base.GetChildren(); + return new NodeBehavior[1] { child }; + } } } \ No newline at end of file diff --git a/Runtime/Core/Model/Node/Decorator.cs b/Runtime/Core/Model/Node/Decorator.cs index af50328..12c9640 100644 --- a/Runtime/Core/Model/Node/Decorator.cs +++ b/Runtime/Core/Model/Node/Decorator.cs @@ -44,7 +44,9 @@ protected virtual void OnStart() protected override Status OnUpdate() { var status = child.Update(); - return OnDecorate(status); + status = OnDecorate(status); + isRunning = status == Status.Running; + return status; } /// /// 装饰子结点返回值 @@ -107,5 +109,14 @@ public sealed override void AddChild(NodeBehavior nodeBehavior) { child = nodeBehavior; } + public sealed override void SetChildren(NodeBehavior[] inChildren) + { + child = inChildren[0]; + } + public sealed override NodeBehavior[] GetChildren() + { + if (child == null) return base.GetChildren(); + return new NodeBehavior[1] { child }; + } } } diff --git a/Runtime/Core/Model/Node/NodeBehavior.cs b/Runtime/Core/Model/Node/NodeBehavior.cs index bc79b68..b678235 100644 --- a/Runtime/Core/Model/Node/NodeBehavior.cs +++ b/Runtime/Core/Model/Node/NodeBehavior.cs @@ -83,6 +83,11 @@ public virtual void AddChild(NodeBehavior _) } public virtual int GetChildrenCount() => 0; public virtual void ClearChildren() { } + public virtual NodeBehavior[] GetChildren() + { + return new NodeBehavior[0]; + } + public virtual void SetChildren(NodeBehavior[] childrem) { } #if UNITY_EDITOR internal NodeData GetSerializedData() { diff --git a/Runtime/Core/Model/Node/NodeData.cs b/Runtime/Core/Model/Node/NodeData.cs index f1f7a18..a44d457 100644 --- a/Runtime/Core/Model/Node/NodeData.cs +++ b/Runtime/Core/Model/Node/NodeData.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using UnityEngine; namespace Kurisu.AkiBT { @@ -14,12 +15,22 @@ public struct NodeType public string _class; public string _ns; public string _asm; + public NodeType(string _class, string _ns, string _asm) + { + this._class = _class; + this._ns = _ns; + this._asm = _asm; + } public NodeType(Type type) { _class = type.Name; _ns = type.Namespace; _asm = type.Assembly.GetName().Name; } + public readonly Type ToType() + { + return Type.GetType(Assembly.CreateQualifiedName(_asm, $"{_ns}.{_class}")); + } public override readonly string ToString() { return $"class:{_class} ns: {_ns} asm:{_asm}"; @@ -34,7 +45,7 @@ public override readonly string ToString() public void Serialize(NodeBehavior nodeBehavior) { nodeType = new NodeType(nodeBehavior.GetType()); - serializedData = BehaviorTreeData.SmartSerialize(nodeBehavior); + serializedData = BehaviorTreeData.SmartSerialize(nodeBehavior, verbose: false); } public NodeData Clone() { diff --git a/Runtime/Core/Model/Node/Root.cs b/Runtime/Core/Model/Node/Root.cs index b4da6b8..303542b 100644 --- a/Runtime/Core/Model/Node/Root.cs +++ b/Runtime/Core/Model/Node/Root.cs @@ -79,5 +79,14 @@ public sealed override void AddChild(NodeBehavior nodeBehavior) { child = nodeBehavior; } + public sealed override void SetChildren(NodeBehavior[] inChildren) + { + child = inChildren[0]; + } + public sealed override NodeBehavior[] GetChildren() + { + if (child == null) return base.GetChildren(); + return new NodeBehavior[1] { child }; + } } } \ No newline at end of file diff --git a/Runtime/Core/Model/Node/Subtree.cs b/Runtime/Core/Model/Node/Subtree.cs index cee740b..3c7c8d0 100644 --- a/Runtime/Core/Model/Node/Subtree.cs +++ b/Runtime/Core/Model/Node/Subtree.cs @@ -54,7 +54,7 @@ public BehaviorTree GetBehaviorTree() public void SetBehaviorTreeData(BehaviorTreeData behaviorTreeData) { - if (!Application.isPlaying || !subtree) return; + if (Application.isPlaying || !subtree) return; subtree.SetBehaviorTreeData(behaviorTreeData); } } diff --git a/Runtime/Core/SceneVariableScope.cs.meta b/Runtime/Core/SceneVariableScope.cs.meta index 71c7e41..c8f1e12 100644 --- a/Runtime/Core/SceneVariableScope.cs.meta +++ b/Runtime/Core/SceneVariableScope.cs.meta @@ -5,7 +5,7 @@ MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 - icon: {instanceID: 0} + icon: {fileID: 2800000, guid: 3570ec2c8a2ccbf43878ea52e8e5e103, type: 3} userData: assetBundleName: assetBundleVariant: diff --git a/Runtime/Core/Utility/APIUpdateConfig.cs b/Runtime/Core/Utility/APIUpdateConfig.cs new file mode 100644 index 0000000..5d91920 --- /dev/null +++ b/Runtime/Core/Utility/APIUpdateConfig.cs @@ -0,0 +1,66 @@ +using System; +using System.Linq; +using UnityEngine; +namespace Kurisu.AkiBT +{ + [CreateAssetMenu(fileName = "APIUpdateConfig", menuName = "AkiBT/APIUpdateConfig")] + public class APIUpdateConfig : ScriptableObject + { + [Serializable] + public class SerializeType + { + private Type type; + public Type Type => type ??= ToType(); + public string nodeType; + private Type ToType() + { + var tokens = nodeType.Split(' '); + return new NodeData.NodeType(tokens[0], tokens[1], tokens[2]).ToType(); + } + public string GetFullTypeName() + { + return $"{Type.Assembly.GetName().Name} {Type.FullName}"; + } + public SerializeType() { } + public SerializeType(Type type) + { + NodeData.NodeType node = new(type); + nodeType = $"{node._class} {node._ns} {node._asm}"; + } + public SerializeType(NodeData.NodeType nodeType) + { + this.nodeType = $"{nodeType._class} {nodeType._ns} {nodeType._asm}"; + } + } + [Serializable] + public class Pair + { + public SerializeType sourceType; + public SerializeType targetType; + public Pair() { } + public Pair(Type sourceType, Type targetType) + { + this.sourceType = new SerializeType(sourceType); + this.targetType = new SerializeType(targetType); + } + } + [field: SerializeField] + public Pair[] Pairs { get; set; } + public Pair FindPair(NodeData.NodeType nodeType) + { + var serializeType = new SerializeType(nodeType); + return Pairs.FirstOrDefault(x => x.sourceType.nodeType == serializeType.nodeType); + } +#if UNITY_EDITOR + internal static APIUpdateConfig GetConfig() + { + var guids = UnityEditor.AssetDatabase.FindAssets($"t:{nameof(APIUpdateConfig)}"); + if (guids.Length == 0) + { + return null; + } + return UnityEditor.AssetDatabase.LoadAssetAtPath(UnityEditor.AssetDatabase.GUIDToAssetPath(guids[0])); + } +#endif + } +} diff --git a/Runtime/Core/Utility/APIUpdateConfig.cs.meta b/Runtime/Core/Utility/APIUpdateConfig.cs.meta new file mode 100644 index 0000000..5b102a5 --- /dev/null +++ b/Runtime/Core/Utility/APIUpdateConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 42a7d5c4480382c42af751a297040ead +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Core/Utility/SharedVariableHelper.cs b/Runtime/Core/Utility/SharedVariableHelper.cs deleted file mode 100644 index 5f9540f..0000000 --- a/Runtime/Core/Utility/SharedVariableHelper.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -namespace Kurisu.AkiBT -{ - public static class SharedVariableHelper - { - private static readonly Dictionary> fieldInfoLookup = new(); - /// - /// Traverse the behavior tree and automatically init all shared variables - /// - /// destination` + /// + /// Variable expression is a special case of property expression. + /// + public class VariableExprAST : PropertyExprAST + { + public VariableExprAST(PropertyInfo metaData, bool isShared, ValueExprAST exprAST) : base(metaData, exprAST) + { + ExprType = ExprType.VariableExpr; + IsShared = isShared; + } + public bool IsShared { get; } + protected internal override ExprAST Accept(ExprVisitor visitor) + { + return visitor.VisitVariableExprAST(this); + } + } +} \ No newline at end of file diff --git a/Runtime/DSL/AST/VariableExprAST.cs.meta b/Runtime/DSL/AST/VariableExprAST.cs.meta new file mode 100644 index 0000000..65d6124 --- /dev/null +++ b/Runtime/DSL/AST/VariableExprAST.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fb6b737befdb7b24e966e8cd5740f5c6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/BuildParserListener.cs b/Runtime/DSL/BuildParserListener.cs new file mode 100644 index 0000000..ac9a070 --- /dev/null +++ b/Runtime/DSL/BuildParserListener.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Pool; +namespace Kurisu.AkiBT.DSL +{ + public sealed class BuildParserListener : IParserListener, IDisposable + { + private static readonly ObjectPool pool = new(() => new()); + private BuildVisitor visitor; + private readonly List variables = new(); + private readonly List nodes = new(); + public BuildParserListener Verbose(bool verbose) + { + visitor.Verbose = verbose; + return this; + } + public void PushTopLevelExpression(NodeExprAST data) + { + visitor.VisitNodeExprAST(data); + nodes.Add(visitor.nodeStack.Pop()); + } + public void PushVariableDefinition(VariableDefineExprAST data) + { + visitor.VisitVariableDefineAST(data); + variables.Add(visitor.variableStack.Pop()); + } + /// + /// Build behavior tree + /// + /// + public BehaviorTree Build() + { + var sequence = new Sequence(); + foreach (var node in nodes) + sequence.AddChild(node); + var instance = new BehaviorTree + { + variables = new List(variables), + root = new Root() { Child = sequence } + }; + variables.Clear(); + nodes.Clear(); + return instance; + } + /// + /// Get pooled build listener + /// + /// The build visitor attached to + /// + public static BuildParserListener GetPooled(BuildVisitor buildVisitor) + { + var listener = pool.Get(); + listener.visitor = buildVisitor; + return listener; + } + public void Dispose() + { + visitor?.Dispose(); + visitor = null; + variables.Clear(); + nodes.Clear(); + pool.Release(this); + } + } +} \ No newline at end of file diff --git a/Runtime/DSL/BuildParserListener.cs.meta b/Runtime/DSL/BuildParserListener.cs.meta new file mode 100644 index 0000000..24d5ac7 --- /dev/null +++ b/Runtime/DSL/BuildParserListener.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b70faae307505748b273444aee9c1b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/BuildVisitor.cs b/Runtime/DSL/BuildVisitor.cs new file mode 100644 index 0000000..04f4727 --- /dev/null +++ b/Runtime/DSL/BuildVisitor.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using UnityEngine.Pool; +using UObject = UnityEngine.Object; +namespace Kurisu.AkiBT.DSL +{ + /// + /// Runtime supported expression visitor to build behavior tree + /// + public class BuildVisitor : ExprVisitor, IDisposable + { + private bool isPooled; + private static readonly ObjectPool pool = new(() => new()); + public bool Verbose { get; set; } + public readonly Stack variableStack = new(); + public readonly Stack nodeStack = new(); + public readonly Stack valueStack = new(); + protected internal override ExprAST VisitNodeExprAST(NodeExprAST node) + { + var nodeInfo = node.MetaData; + var instance = Activator.CreateInstance(nodeInfo.GetNodeType()) as NodeBehavior; + // Push if root + if (nodeStack.Count == 0) + nodeStack.Push(instance); + // Push to node stack for writing properties + nodeStack.Push(instance); + base.VisitNodeExprAST(node); + // Push to value stack for using as value expr + valueStack.Push(instance); + nodeStack.Pop(); + if (Verbose) Log($"Succeed build node {nodeInfo.GetNodeType().Name}"); + return node; + } + protected internal override ExprAST VisitPropertyAST(PropertyExprAST node) + { + NodeBehavior parent = nodeStack.Peek(); + base.VisitPropertyAST(node); + FieldInfo fieldInfo = node.MetaData.FieldInfo; + // Pop value from stack + var boxValue = valueStack.Pop(); + Type boxType = boxValue.GetType(); + if (boxType != fieldInfo.FieldType) + { + boxValue = NodeTypeRegistry.Cast(in boxValue, boxType, fieldInfo.FieldType); + } + fieldInfo.SetValue(parent, boxValue); + if (Verbose) Log($"Write property {fieldInfo.Name} to {parent}"); + return node; + } + protected internal override ExprAST VisitValueExprAST(ValueExprAST node) + { + base.VisitValueExprAST(node); + // Push value to task + valueStack.Push(node.Value); + return node; + } + protected internal override ExprAST VisitArrayExprAST(ArrayExprAST node) + { + IList array = Activator.CreateInstance(node.FieldType, node.Children.Count) as IList; + if (array.IsFixedSize) + { + int i = 0; + foreach (var child in node.Children) + { + Visit(child); + array[i++] = valueStack.Pop(); + } + } + else + { + foreach (var child in node.Children) + { + Visit(child); + array.Add(valueStack.Pop()); + } + } + valueStack.Push(array); + return node; + } + protected internal override ExprAST VisitVariableDefineAST(VariableDefineExprAST node) + { + base.VisitVariableDefineAST(node); + SharedVariable variable = CreateInstance(node.Type); + variable.Name = node.Name; + variable.IsGlobal = node.IsGlobal; + variable.IsExposed = node.IsGlobal; + var value = valueStack.Pop(); + if (!NodeTypeRegistry.IsFieldType(value, node.Type)) + { + value = NodeTypeRegistry.Cast(in value, value.GetType(), NodeTypeRegistry.GetValueType(node.Type)); + } + variable.SetValue(value); + if (Verbose) Log($"Build variable {variable.Name}, type: {node.Type}, value: {variable.GetValue()}"); + variableStack.Push(variable); + return node; + } + protected internal override ExprAST VisitObjectDefineAST(ObjectDefineExprAST node) + { + base.VisitObjectDefineAST(node); + SharedObject variable = CreateInstance(node.Type) as SharedObject; + variable.Name = node.Name; + variable.IsGlobal = node.IsGlobal; + variable.IsExposed = node.IsGlobal; + variable.ConstraintTypeAQN = node.ConstraintTypeAQN; + WriteSharedObjectValue(variable, valueStack.Pop() as string); + if (Verbose) Log($"Build variable {variable.Name}, type: {node.Type}, value: {variable.GetValue()}"); + variableStack.Push(variable); + return node; + } + /// + /// Override to implement UnityEngine.Object parse logic at runtime, such as loading by address + /// + /// + /// + protected virtual void WriteSharedObjectValue(IBindableVariable sharedObject, string value) + { + // Default parse as guid in Editor +#if UNITY_EDITOR + var path = UnityEditor.AssetDatabase.GUIDToAssetPath(value); + if (!string.IsNullOrEmpty(path)) + { + sharedObject.Value = UnityEditor.AssetDatabase.LoadAssetAtPath(path); + } + else +#endif + { + sharedObject.Value = null; + } + } + protected internal override ExprAST VisitVariableExprAST(VariableExprAST node) + { + NodeBehavior parent = nodeStack.Peek(); + base.VisitPropertyAST(node); + FieldInfo fieldInfo = node.MetaData.FieldInfo; + // This will also create instance for SharedTObject + SharedVariable variable = Activator.CreateInstance(fieldInfo.FieldType) as SharedVariable; + if (node.IsShared) + { + variable.Name = valueStack.Pop() as string; + variable.IsShared = true; + } + else + { + // SharedTObject or SharedObject + if (variable is IBindableVariable sharedObject) + { + WriteSharedObjectValue(sharedObject, valueStack.Pop() as string); + } + else + { + variable.SetValue(valueStack.Pop()); + } + } + fieldInfo.SetValue(parent, variable); + return node; + } + /// + /// Create a instance by field type + /// + /// + /// + private static SharedVariable CreateInstance(FieldType type) + { + return type switch + { + FieldType.Int => new SharedInt(), + FieldType.Float => new SharedFloat(), + FieldType.Bool => new SharedBool(), + FieldType.String => new SharedString(), + FieldType.Vector2 => new SharedVector2(), + FieldType.Vector3 => new SharedVector3(), + FieldType.Vector2Int => new SharedVector2Int(), + FieldType.Vector3Int => new SharedVector3Int(), + FieldType.Object => new SharedObject(), + _ => throw new ArgumentException(nameof(type)), + }; + } + protected static void Log(string message) + { + Debug.Log($"[Build Visitor] {message}"); + } + public static BuildVisitor GetPooled() + { + var visitor = pool.Get(); + visitor.isPooled = true; + return visitor; + } + public void Dispose() + { + variableStack.Clear(); + valueStack.Clear(); + nodeStack.Clear(); + // Only release visitor from pool + if (isPooled) + { + isPooled = false; + pool.Release(this); + } + } + } +} \ No newline at end of file diff --git a/Runtime/DSL/BuildVisitor.cs.meta b/Runtime/DSL/BuildVisitor.cs.meta new file mode 100644 index 0000000..a174672 --- /dev/null +++ b/Runtime/DSL/BuildVisitor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 596aa48943305a349bab95d6964365a5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Compiler.cs b/Runtime/DSL/Compiler.cs new file mode 100644 index 0000000..004b0e7 --- /dev/null +++ b/Runtime/DSL/Compiler.cs @@ -0,0 +1,77 @@ +namespace Kurisu.AkiBT.DSL +{ + /// + /// Default compiler, used to demonstrate API combination and calling + /// + public class Compiler + { + private readonly NodeTypeRegistry registry; + private bool verbose; + public Compiler(NodeTypeRegistry registry) + { + this.registry = registry; + } + public Compiler(string registryPath) + { + registry = NodeTypeRegistry.FromPath(registryPath); + } + public BehaviorTree Compile(string code) + { + var lexer = new Lexer(new Reader(code), registry).Verbose(verbose); + var bpl = BuildParserListener.GetPooled(BuildVisitor.GetPooled()).Verbose(verbose); + try + { + lexer.ParseToEnd(new Parser(lexer, bpl)); + return bpl.Build(); + } + finally + { + bpl.Dispose(); + } + } + public Compiler Verbose(bool verbose) + { + this.verbose = verbose; + return this; + } + } + public static class LexerExtensions + { + /// + /// Parse all token until end from lexer + /// + /// + /// + public static void ParseToEnd(this Lexer lexer, IParser parser) + { + lexer.GetNextToken(); + while (true) + { + switch (lexer.CurrentToken.Type) + { + case TokenType.EOF: + return; + case TokenType.COMMENT: + DrainComment(); + break; + case TokenType.DEF_VARIABLE: + parser.HandleVariableDefinition(); + break; + default: + parser.HandleTopLevelExpression(); + break; + } + lexer.GetNextToken(); + } + void DrainComment() + { + lexer.GetNextToken(); + // Skip token within comment + while (lexer.CurrentToken != TokenType.COMMENT) + { + lexer.GetNextToken(); + } + } + } + } +} diff --git a/Runtime/DSL/Compiler.cs.meta b/Runtime/DSL/Compiler.cs.meta new file mode 100644 index 0000000..6e74ebe --- /dev/null +++ b/Runtime/DSL/Compiler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ed143f832f3ae254d99525d6c048963e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Contract.meta b/Runtime/DSL/Contract.meta new file mode 100644 index 0000000..20028c5 --- /dev/null +++ b/Runtime/DSL/Contract.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9420c792f56640244992c1e3f738dc5a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Contract/VectorContract.cs b/Runtime/DSL/Contract/VectorContract.cs new file mode 100644 index 0000000..90ea79a --- /dev/null +++ b/Runtime/DSL/Contract/VectorContract.cs @@ -0,0 +1,54 @@ +using System; +using UnityEngine; +namespace Kurisu.AkiBT.DSL +{ + public class Vector3IntToVector3Contract : ITypeContract + { + public bool CanConvert(Type inputType, Type expectType) + { + return (inputType == typeof(Vector3Int)) && expectType == typeof(Vector3); + } + + public object Convert(in object value, Type inputType, Type expectType) + { + return (Vector3)(Vector3Int)value; + } + } + public class Vector2ToVector3Contract : ITypeContract + { + public bool CanConvert(Type inputType, Type expectType) + { + return (inputType == typeof(Vector2)) && expectType == typeof(Vector3); + } + + public object Convert(in object value, Type inputType, Type expectType) + { + return (Vector3)(Vector2)value; + } + } + + public class Vector3ToVector2Contract : ITypeContract + { + public bool CanConvert(Type inputType, Type expectType) + { + return (inputType == typeof(Vector3)) && expectType == typeof(Vector2); + } + + public object Convert(in object value, Type inputType, Type expectType) + { + return (Vector2)(Vector3)value; + } + } + public class Vector2IntToVector2Contract : ITypeContract + { + public bool CanConvert(Type inputType, Type expectType) + { + return (inputType == typeof(Vector2Int)) && expectType == typeof(Vector2); + } + + public object Convert(in object value, Type inputType, Type expectType) + { + return (Vector2)(Vector2Int)value; + } + } +} \ No newline at end of file diff --git a/Runtime/DSL/Contract/VectorContract.cs.meta b/Runtime/DSL/Contract/VectorContract.cs.meta new file mode 100644 index 0000000..f81e069 --- /dev/null +++ b/Runtime/DSL/Contract/VectorContract.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb9f8f2fdfe599c40931c83f78301aae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Decompiler.cs b/Runtime/DSL/Decompiler.cs new file mode 100644 index 0000000..251af23 --- /dev/null +++ b/Runtime/DSL/Decompiler.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +#if UNITY_EDITOR +using UnityEditor; +#endif +namespace Kurisu.AkiBT.DSL +{ + /// + /// Simple decompiler to convert any behavior tree to DSL + /// + public class Decompiler + { + private readonly StringBuilder stringBuilder = new(); + public string Decompile(IBehaviorTreeContainer behaviorTreeContainer) + { + var instance = behaviorTreeContainer.GetBehaviorTree(); + stringBuilder.Clear(); + WriteReferencedVariable(instance); + WriteNode(instance.root.Child, 0); + return stringBuilder.ToString(); + } + private void WriteReferencedVariable(BehaviorTree behaviorTreeContainer) + { + foreach (var variable in behaviorTreeContainer.SharedVariables) + { + if (variable.IsGlobal) + Write($"${variable.GetType().Name[6..]}$"); + else + Write(variable.GetType().Name[6..]); + Space(); + Write(variable.Name); + Space(); + var value = variable.GetValue(); + if (variable is SharedObject sharedObject && !string.IsNullOrEmpty(sharedObject.ConstraintTypeAQN)) + { + Type constraintType = Type.GetType(sharedObject.ConstraintTypeAQN); + SafeWrite($"{constraintType.Assembly.GetName().Name}, {constraintType.Namespace}.{constraintType.Name}"); + Space(); + } +#if UNITY_EDITOR + if (value is UnityEngine.Object UObject) + { + string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(UObject)); + if (string.IsNullOrEmpty(guid)) Write("Null"); + else Write(AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(UObject))); + } + else +#endif + { + SafeWrite(value); + } + NewLine(); + } + } + private void WriteNode(NodeBehavior node, int indentLevel) + { + WriteIndent(indentLevel); + var type = node.GetType(); + Write(type.Name); + Write('('); + WriteProperties(type, node, indentLevel); + Write(')'); + } + private void WriteProperties(Type type, NodeBehavior node, int indentLevel) + { + bool haveProperty = false; + var properties = NodeTypeRegistry.GetAllFields(type).ToList(); + for (var i = 0; i < properties.Count; i++) + { + var p = properties[i]; + var value = p.GetValue(node); + if (value == null) continue; + if (haveProperty) + { + Write(','); + } + else + { + haveProperty = true; + } + WritePropertyName(p); + var fieldType = p.FieldType; + if (IsIList(fieldType, out Type elementType)) + { + Write(':'); + WriteArray(value as IList, elementType, indentLevel); + } + else if (fieldType.IsSubclassOf(typeof(NodeBehavior)) || fieldType == typeof(NodeBehavior)) + { + Write(':'); + NewLine(); + WriteNode(value as NodeBehavior, indentLevel + 1); + } + else if (fieldType.IsSubclassOf(typeof(SharedVariable)) || fieldType == typeof(SharedVariable)) + { + WriteVariableValue(value as SharedVariable); + } + else + { + WritePropertyValue(value); + } + } + } + private static bool IsIList(Type type, out Type elementType) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) + { + elementType = type.GetGenericArguments()[0]; + return true; + } + if (type.IsArray) + { + elementType = type.GetElementType(); + return true; + } + elementType = null; + return false; + } + private void WriteArray(IList list, Type childType, int indentLevel) + { + Write('['); + for (var i = 0; i < list.Count; i++) + { + if (i != 0) Write(','); + var value = list[i]; + if (childType.IsSubclassOf(typeof(NodeBehavior)) || childType == typeof(NodeBehavior)) + { + NewLine(); + WriteNode(value as NodeBehavior, indentLevel + 1); + } + else if (childType.IsSubclassOf(typeof(SharedVariable)) || childType == typeof(SharedVariable)) + { + // TODO: + } + else + { + SafeWrite(value); + } + } + Write(']'); + } + private void WritePropertyName(FieldInfo fieldInfo) + { + AkiLabelAttribute label; + if ((label = fieldInfo.GetCustomAttribute()) != null) + { + Write(label.Title); + } + else + { + Write(fieldInfo.Name); + } + } + private void WriteVariableValue(SharedVariable variable) + { + if (variable.IsShared) + { + Write("=>"); + Write(variable.Name); + } + else + { + Write(':'); + SafeWrite(variable.GetValue()); + } + } + private void WritePropertyValue(object value) + { + Write(':'); + SafeWrite(value); + } + private void Space() + { + stringBuilder.Append(' '); + } + private void WriteIndent(int indentLevel) + { + stringBuilder.Append(' ', indentLevel * 4); + } + private void NewLine() + { + stringBuilder.Append('\n'); + } + private void SafeWrite(object context) + { + if (context is string) + stringBuilder.Append($"\"{context}\""); + else + stringBuilder.Append(context.ToString()); + } + private void Write(string text) + { + stringBuilder.Append(text); + } + private void Write(char token) + { + stringBuilder.Append(token); + } + } +} diff --git a/Runtime/DSL/Decompiler.cs.meta b/Runtime/DSL/Decompiler.cs.meta new file mode 100644 index 0000000..33576f5 --- /dev/null +++ b/Runtime/DSL/Decompiler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2f41311136a18564880b4531c585045d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Interface.meta b/Runtime/DSL/Interface.meta new file mode 100644 index 0000000..88c3d9e --- /dev/null +++ b/Runtime/DSL/Interface.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b522c136c237acf478bcd1c5377d848a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Interface/IParser.cs b/Runtime/DSL/Interface/IParser.cs new file mode 100644 index 0000000..f8391b4 --- /dev/null +++ b/Runtime/DSL/Interface/IParser.cs @@ -0,0 +1,9 @@ +namespace Kurisu.AkiBT.DSL +{ + public interface IParser + { + void HandleVariableDefinition(); + + void HandleTopLevelExpression(); + } +} \ No newline at end of file diff --git a/Runtime/DSL/Interface/IParser.cs.meta b/Runtime/DSL/Interface/IParser.cs.meta new file mode 100644 index 0000000..0fe181e --- /dev/null +++ b/Runtime/DSL/Interface/IParser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 051245cb682d03b4c9b3aeab7e4f9f90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Interface/IParserListener.cs b/Runtime/DSL/Interface/IParserListener.cs new file mode 100644 index 0000000..139ed7a --- /dev/null +++ b/Runtime/DSL/Interface/IParserListener.cs @@ -0,0 +1,8 @@ +namespace Kurisu.AkiBT.DSL +{ + public interface IParserListener + { + void PushVariableDefinition(VariableDefineExprAST data); + void PushTopLevelExpression(NodeExprAST data); + } +} \ No newline at end of file diff --git a/Runtime/DSL/Interface/IParserListener.cs.meta b/Runtime/DSL/Interface/IParserListener.cs.meta new file mode 100644 index 0000000..bbf429c --- /dev/null +++ b/Runtime/DSL/Interface/IParserListener.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d647a5bca06ec3148a114f01d5a69f31 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Interface/ITypeContract.cs b/Runtime/DSL/Interface/ITypeContract.cs new file mode 100644 index 0000000..ec7b4a2 --- /dev/null +++ b/Runtime/DSL/Interface/ITypeContract.cs @@ -0,0 +1,9 @@ +using System; +namespace Kurisu.AkiBT.DSL +{ + public interface ITypeContract + { + bool CanConvert(Type inputType, Type expectType); + object Convert(in object value, Type inputType, Type expectType); + } +} \ No newline at end of file diff --git a/Runtime/DSL/Interface/ITypeContract.cs.meta b/Runtime/DSL/Interface/ITypeContract.cs.meta new file mode 100644 index 0000000..e0949c0 --- /dev/null +++ b/Runtime/DSL/Interface/ITypeContract.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0453cc21e4c43fc49a683a38ec1186aa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Kurisu.AkiBT.DSL.asmdef b/Runtime/DSL/Kurisu.AkiBT.DSL.asmdef new file mode 100644 index 0000000..a04d98b --- /dev/null +++ b/Runtime/DSL/Kurisu.AkiBT.DSL.asmdef @@ -0,0 +1,16 @@ +{ + "name": "Kurisu.AkiBT.DSL", + "rootNamespace": "Kurisu.AkiBT.DSL", + "references": [ + "GUID:bf89a8bf6625fad4eb6879c01e73541d" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Runtime/DSL/Kurisu.AkiBT.DSL.asmdef.meta b/Runtime/DSL/Kurisu.AkiBT.DSL.asmdef.meta new file mode 100644 index 0000000..e2efabf --- /dev/null +++ b/Runtime/DSL/Kurisu.AkiBT.DSL.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ea13d7331a0df234f85e126949843072 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Lexer.cs b/Runtime/DSL/Lexer.cs new file mode 100644 index 0000000..edace6a --- /dev/null +++ b/Runtime/DSL/Lexer.cs @@ -0,0 +1,243 @@ +using UnityEngine; +namespace Kurisu.AkiBT.DSL +{ + public sealed class Lexer + { + private readonly Reader reader; + private readonly NodeTypeRegistry registry; + private NodeInfo nodeInfo; + private FieldType valueType; + public Token CurrentToken { get; private set; } + private ValueExprAST value; + private bool verbose; + public Lexer(Reader reader, NodeTypeRegistry nodeTypeRegistry) + { + this.reader = reader; + registry = nodeTypeRegistry; + } + public Lexer Verbose(bool verbose) + { + this.verbose = verbose; + return this; + } + public Token GetNextToken() + { + var word = reader.Read(); + if (word == null) + { + CurrentToken = new(TokenType.EOF); + } + else if (word == Symbol.Comment) + { + CurrentToken = new(TokenType.COMMENT); + } + else if (TryParseVariableType(word, out FieldType type)) + { + valueType = type; + CurrentToken = new(TokenType.DEF_VARIABLE, word); + } + else if (registry.TryGetNode(word, out NodeInfo info)) + { + nodeInfo = info; + CurrentToken = new(TokenType.DEF_NODE, word); + } + else if (TryParseValue(word)) + { + valueType = value.Type; + CurrentToken = new(TokenType.VALUE, value.Value.ToString()); + } + else + { + CurrentToken = new Token(word); + } + if (verbose) Log(CurrentToken); + return CurrentToken; + } + public ValueExprAST GetLastValue() + { + return value; + } + public NodeInfo GetLastNodeInfo() + { + return nodeInfo; + } + public FieldType GetLastValueType() + { + return valueType; + } + private bool TryParseVariableType(string token, out FieldType valueType) + { + valueType = default; + if (token.Length < 3) return false; + if (token[0] == '$' && token[^1] == '$') + { + return TryParseVariableType(token[1..^1], out valueType); + } + else + { + return TryParseVariableType(token, out valueType); + } + static bool TryParseVariableType(string token, out FieldType valueType) + { + switch (token) + { + case Symbol.Int: + { + valueType = FieldType.Int; + return true; + } + case Symbol.Bool: + { + valueType = FieldType.Bool; + return true; + } + case Symbol.Float: + { + valueType = FieldType.Float; + return true; + } + case Symbol.String: + { + valueType = FieldType.String; + return true; + } + case Symbol.Vector2: + { + valueType = FieldType.Vector2; + return true; + } + case Symbol.Vector2Int: + { + valueType = FieldType.Vector2Int; + return true; + } + case Symbol.Vector3: + { + valueType = FieldType.Vector3; + return true; + } + case Symbol.Vector3Int: + { + valueType = FieldType.Vector3Int; + return true; + } + case Symbol.Object: + { + valueType = FieldType.Object; + return true; + } + } + valueType = default; + return false; + } + } + private bool TryParseValue(string token) + { + if (int.TryParse(token, out int intNum)) + { + value = new(FieldType.Int, intNum); + return true; + } + if (float.TryParse(token, out float floatNum)) + { + value = new(FieldType.Float, floatNum); + return true; + } + if (bool.TryParse(token, out bool boolValue)) + { + value = new(FieldType.Bool, boolValue); + return true; + } + // Aligned with reader index + reader.MoveBack(); + if (TryParseVector2Int(out Vector2Int vector2Int)) + { + value = new(FieldType.Vector2Int, vector2Int); + return true; + } + if (TryParseVector2(out Vector2 vector2)) + { + value = new(FieldType.Vector2, vector2); + return true; + } + if (TryParseVector3Int(out Vector3Int vector3Int)) + { + value = new(FieldType.Vector3Int, vector3Int); + return true; + } + if (TryParseVector3(out Vector3 vector3)) + { + value = new(FieldType.Vector3, vector3); + return true; + } + // Missing string value? + // Special case here since we can not know whether it is a symbol here + reader.MoveNext(); + return false; + } + public bool TryParseVector2(out Vector2 vector2) + { + int bt = reader.CurrentIndex; + try + { + vector2 = reader.ReadVector2(); + return true; + } + catch + { + reader.MoveTo(bt); + vector2 = default; + return false; + } + } + public bool TryParseVector3(out Vector3 vector3) + { + int bt = reader.CurrentIndex; + try + { + vector3 = reader.ReadVector3(); + return true; + } + catch + { + reader.MoveTo(bt); + vector3 = default; + return false; + } + } + public bool TryParseVector3Int(out Vector3Int vector3Int) + { + int bt = reader.CurrentIndex; + try + { + vector3Int = reader.ReadVector3Int(); + return true; + } + catch + { + reader.MoveTo(bt); + vector3Int = default; + return false; + } + } + public bool TryParseVector2Int(out Vector2Int vector2Int) + { + int bt = reader.CurrentIndex; + try + { + vector2Int = reader.ReadVector2Int(); + return true; + } + catch + { + reader.MoveTo(bt); + vector2Int = default; + return false; + } + } + private static void Log(Token token) + { + Debug.Log($"[Lexer] Read token: {token}"); + } + } +} \ No newline at end of file diff --git a/Runtime/DSL/Lexer.cs.meta b/Runtime/DSL/Lexer.cs.meta new file mode 100644 index 0000000..5f892c2 --- /dev/null +++ b/Runtime/DSL/Lexer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66d563085abc1d74a95e4addf0613c75 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/NodeTypeRegistry.cs b/Runtime/DSL/NodeTypeRegistry.cs new file mode 100644 index 0000000..ebd3468 --- /dev/null +++ b/Runtime/DSL/NodeTypeRegistry.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Newtonsoft.Json; +using System.IO; +using UnityEngine; +using UObject = UnityEngine.Object; +using UnityEngine.Assertions; +namespace Kurisu.AkiBT.DSL +{ + public class NodeTypeRegistry + { + [JsonProperty] + private Dictionary NodeInfos { get; set; } = new(); + public static readonly HashSet contracts = new(); + static NodeTypeRegistry() + { + contracts.Add(new Vector2IntToVector2Contract()); + contracts.Add(new Vector3IntToVector3Contract()); + contracts.Add(new Vector2ToVector3Contract()); + contracts.Add(new Vector3ToVector2Contract()); + } + public static NodeTypeRegistry FromPath(string path) + { + return JsonConvert.DeserializeObject(File.ReadAllText(path)); + } + /// + /// Get node meta data from node path + /// + /// + /// + /// + public bool TryGetNode(string nodePath, out NodeInfo metaData) + { + if (NodeInfos.TryGetValue(nodePath, out metaData)) + { + // Lazy call to cache all fields + metaData.GetNodeType(); + return !metaData.isVariable; + } + return false; + } + /// + /// Register a new node + /// + /// + /// + public void SetNode(string nodePath, NodeInfo metaData) + { + NodeInfos[nodePath] = metaData; + } + public static object Cast(in object value, Type inputType, Type expectType) + { + foreach (var contract in contracts) + { + if (contract.CanConvert(inputType, expectType)) + { + return contract.Convert(value, inputType, expectType); + } + } + return value; + } + /// + /// Get from type + /// + /// + /// + public static FieldType GetFieldType(Type type) + { + if (type.IsSubclassOf(typeof(SharedVariable))) return FieldType.Variable; + if (type.IsEnum) return FieldType.Enum; + if (type == typeof(int)) return FieldType.Int; + if (type == typeof(float)) return FieldType.Float; + if (type == typeof(bool)) return FieldType.Bool; + if (type == typeof(string)) return FieldType.String; + if (type == typeof(Vector2)) return FieldType.Vector2; + if (type == typeof(Vector2Int)) return FieldType.Vector2Int; + if (type == typeof(Vector3)) return FieldType.Vector3; + if (type == typeof(Vector3Int)) return FieldType.Vector3Int; + if (type.IsSubclassOf(typeof(UObject))) return FieldType.Object; + return FieldType.Unknown; + } + /// + /// Get value type from + /// + /// + /// + public static Type GetValueType(FieldType fieldType) + { + Assert.IsTrue((int)fieldType <= 7); + return fieldType switch + { + FieldType.Int => typeof(int), + FieldType.Float => typeof(float), + FieldType.Bool => typeof(bool), + FieldType.Vector2 => typeof(Vector2), + FieldType.Vector2Int => typeof(Vector2Int), + FieldType.Vector3 => typeof(Vector3), + FieldType.Vector3Int => typeof(Vector3Int), + FieldType.String => typeof(string), + _ => throw new ArgumentOutOfRangeException(nameof(fieldType)), + }; + } + public static bool IsFieldType(object value, FieldType fieldType) + { + return fieldType switch + { + FieldType.Variable => value is SharedObject, + FieldType.Enum => value is Enum, + FieldType.Int => value is int, + FieldType.Float => value is float, + FieldType.Bool => value is bool, + FieldType.String => value is string, + FieldType.Vector2 => value is Vector2, + FieldType.Vector2Int => value is Vector2Int, + FieldType.Vector3 => value is Vector3, + FieldType.Vector3Int => value is Vector3Int, + FieldType.Object => value is UObject, + FieldType.Unknown => false, + _ => throw new ArgumentOutOfRangeException(nameof(fieldType)), + }; + } + public static IEnumerable GetAllFields(Type type) + { + return type.GetFields(BindingFlags.Public | BindingFlags.Instance) + .Concat(GetAllFields_Internal(type)) + .Where(field => field.IsInitOnly == false && field.GetCustomAttribute() == null) + .ToList(); + } + private static IEnumerable GetAllFields_Internal(Type t) + { + if (t == null) + return Enumerable.Empty(); + + return t.GetFields(BindingFlags.NonPublic | BindingFlags.Instance) + .Where(field => field.GetCustomAttribute() != null || field.GetCustomAttribute() != null) + .Concat(GetAllFields_Internal(t.BaseType)); + } + } + public class NodeInfo + { + public string className; + public string ns; + public string asm; + public bool isVariable; + public List properties; + private List fieldInfos; + private Type type; + public PropertyInfo GetProperty(string label) + { + return properties?.FirstOrDefault(x => x.label == label || x.name == label); + } + public Type GetNodeType() + { + type ??= Type.GetType(Assembly.CreateQualifiedName(asm, $"{ns}.{className}")); + if (fieldInfos == null) + { + fieldInfos = NodeTypeRegistry.GetAllFields(type).ToList(); + foreach (var property in properties) + { + var field = fieldInfos.FirstOrDefault(x => x.Name == property.name); + if (field != null) + property.FieldInfo = field; + } + } + return type; + } + } + public class PropertyInfo + { + public string label; + public string name; + public FieldType fieldType; + [JsonIgnore] + public bool IsVariable => fieldType == FieldType.Variable; + [JsonIgnore] + public bool IsEnum => fieldType == FieldType.Enum; + [JsonIgnore] + public FieldInfo FieldInfo { get; internal set; } + } +} diff --git a/Runtime/DSL/NodeTypeRegistry.cs.meta b/Runtime/DSL/NodeTypeRegistry.cs.meta new file mode 100644 index 0000000..722151f --- /dev/null +++ b/Runtime/DSL/NodeTypeRegistry.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6bfcbcd511ef3904b801705c93c26a3d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Parser.cs b/Runtime/DSL/Parser.cs new file mode 100644 index 0000000..8f43134 --- /dev/null +++ b/Runtime/DSL/Parser.cs @@ -0,0 +1,351 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text.RegularExpressions; +using UnityEngine; +using UnityEngine.Assertions; +namespace Kurisu.AkiBT.DSL +{ + public sealed class Parser : IParser + { + private const string FullAQNPattern = @"^[A-Za-z0-9\.]+,\s?[A-Za-z0-9\.]+,\s?Version=\d+\.\d+\.\d+\.\d+,\s?Culture=[a-zA-Z]+,\s?PublicKeyToken=[a-zA-Z0-9]+"; + private const string HalfAQNPattern = @"^[A-Za-z0-9\.]+,\s?[A-Za-z0-9\.]+"; + private readonly Lexer lexer; + private readonly IParserListener parserListener; + public Parser(Lexer lexer, IParserListener parserListener) + { + this.lexer = lexer; + this.parserListener = parserListener; + } + public void HandleVariableDefinition() + { + var variableAST = ParseVariableDefine(); + if (variableAST != null) + { + parserListener.PushVariableDefinition(variableAST); + } + else + { + // Skip token for error recovery. + lexer.GetNextToken(); + } + } + public void HandleTopLevelExpression() + { + var nodeAST = ParseNode(); + if (nodeAST != null) + { + parserListener.PushTopLevelExpression(nodeAST); + } + else + { + // Skip token for error recovery. + lexer.GetNextToken(); + } + } + private NodeExprAST ParseNode() + { + if (lexer.CurrentToken != TokenType.DEF_NODE) + { + LogError("Expected define node type"); + return null; + } + // node info is cached in scanner + var metaData = lexer.GetLastNodeInfo(); + if (lexer.GetNextToken() != Symbol.LeftParenthesis) + { + LogError("Expected '(' in prototype"); + return null; + } + var properties = new List(); + if (lexer.GetNextToken() != Symbol.RightParenthesis) + { + while (true) + { + PropertyExprAST property = ParseProperty(metaData); + if (property == null) + { + return null; + } + properties.Add(property); + if (lexer.CurrentToken == Symbol.RightParenthesis) + { + break; + } + if (lexer.CurrentToken != Symbol.Comma) + { + LogError("Expected ')' or ',' in property list"); + return null; + } + lexer.GetNextToken(); + } + } + // eat ')' + lexer.GetNextToken(); + return new NodeExprAST(metaData, properties); + } + private PropertyExprAST ParseProperty(NodeInfo nodeInfo) + { + Assert.IsNotNull(nodeInfo); + if (lexer.CurrentToken != TokenType.DYNAMIC) + { + LogError("Expected dynamic token"); + return null; + } + // property need register in NodeTypeRegistry to identify right value type + var propertyInfo = nodeInfo.GetProperty(lexer.CurrentToken.Value); + if (propertyInfo == null) + { + LogError("Expected valid property"); + return null; + } + var fieldInfo = propertyInfo.FieldInfo; + if (fieldInfo == null) + { + LogError($"Field {propertyInfo.name} not exist in {nodeInfo.GetNodeType()}"); + return null; + } + Type fieldType = fieldInfo.FieldType; + lexer.GetNextToken(); + // Thanks to meta data, easy to find out here + if (propertyInfo.IsVariable) + { + return ParseVariable(); + } + // Special case for enum + else if (propertyInfo.IsEnum) + { + return ParseEnum(); + } + else + { + return ParseExpr(); + } + PropertyExprAST ParseExpr() + { + if (lexer.CurrentToken != Symbol.Colon) + { + LogError($"Expected {Symbol.Colon} after define property name"); + return null; + } + lexer.GetNextToken(); + ExprAST expr = ParseExpression(fieldType); + if (expr == null) + { + LogError("Expected property value"); + return null; + } + return new PropertyExprAST(propertyInfo, expr); + } + // Handle special case for enum + PropertyExprAST ParseEnum() + { + if (lexer.CurrentToken != Symbol.Colon) + { + LogError($"Expected {Symbol.Colon} after define property name"); + return null; + } + lexer.GetNextToken(); + ValueExprAST expr; + if (lexer.CurrentToken == TokenType.VALUE && lexer.GetLastValueType() == FieldType.Int) + { + expr = new(FieldType.Enum, lexer.GetLastValue().Value); + } + else + { + if (Enum.TryParse(fieldType, lexer.CurrentToken.Value, out object enumValue)) + { + expr = new(FieldType.Enum, enumValue); + } + else + { + LogError($"Can not parse enum {fieldType} from {lexer.CurrentToken.Value}"); + return null; + } + } + //eat ')' or ',' + lexer.GetNextToken(); + return new PropertyExprAST(propertyInfo, expr); + } + // Handle special case for variable + VariableExprAST ParseVariable() + { + bool isShared; + if (lexer.CurrentToken == Symbol.Shared) + { + isShared = true; + } + else + { + isShared = false; + if (lexer.CurrentToken != Symbol.Colon) + { + LogError($"Expected {Symbol.Colon} after define property name"); + return null; + } + } + lexer.GetNextToken(); + ValueExprAST value; + if (lexer.CurrentToken == TokenType.VALUE) + { + value = ParseValue(); + if (value == null) + { + LogError("Expected value"); + return null; + } + // TODO: Not type safe + return new VariableExprAST(propertyInfo, isShared, value); + } + else + { + // Identify reference type [String|UObject] as string => can not identify UObject in parser which missing meta data + value = ValueExprAST.String(lexer.CurrentToken.Value); + //eat ')' or ',' + lexer.GetNextToken(); + return new VariableExprAST(propertyInfo, isShared, value); + } + } + } + private ExprAST ParseExpression(Type fieldType) + { + if (lexer.CurrentToken == Symbol.LeftBracket) + { + var array = ParseArray(fieldType); + if (array == null) + { + LogError("Expected array"); + return null; + } + return array; + } + // Reference value + else if (lexer.CurrentToken == TokenType.DEF_NODE) + { + var node = ParseNode(); + if (node == null) + { + LogError("Expected node"); + return null; + } + return node; + } + // Simple value + else if (lexer.CurrentToken == TokenType.VALUE) + { + var valueExp = ParseValue(); + if (valueExp == null) + { + LogError("Expected value"); + return null; + } + // TODO: Not type safe + return valueExp; + } + // Identify reference type [String|UObject] as string => can not identify UObject in parser which missing meta data + return ValueExprAST.String(lexer.CurrentToken.Value); + } + private ValueExprAST ParseValue() + { + if (lexer.CurrentToken != TokenType.VALUE) + { + LogError("Expected value token"); + return null; + } + var valueExp = lexer.GetLastValue(); + if (valueExp == null) + { + LogError("Expected value"); + return null; + } + lexer.GetNextToken(); + return valueExp; + } + private ArrayExprAST ParseArray(Type fieldType) + { + if (lexer.CurrentToken != Symbol.LeftBracket) + { + LogError($"Expected {Symbol.LeftBracket} before array start"); + return null; + } + var values = new List(); + if (lexer.GetNextToken() != Symbol.RightBracket) + { + while (true) + { + // Assert containing generic argument + ExprAST value = ParseExpression(fieldType.GetGenericArguments()[0]); + if (value == null) + { + return null; + } + values.Add(value); + if (lexer.CurrentToken == Symbol.RightBracket) + { + break; + } + if (lexer.CurrentToken != Symbol.Comma) + { + LogError("Expected ']' or ',' in array"); + return null; + } + lexer.GetNextToken(); + } + } + // eat `]` + lexer.GetNextToken(); + return new ArrayExprAST(fieldType, values); + } + private VariableDefineExprAST ParseVariableDefine() + { + if (lexer.CurrentToken != TokenType.DEF_VARIABLE) + { + LogError("Expected define variable type"); + return null; + } + var word = lexer.CurrentToken.Value; + bool isGlobal = word[0] == '$' && word[^1] == '$'; + FieldType valueType = lexer.GetLastValueType(); + string name = lexer.GetNextToken().Value; + var token = lexer.GetNextToken(); + if (token.Type == TokenType.VALUE) + { + if (lexer.GetLastValueType() != valueType) + LogWarning($"Expected value type {valueType.ToString().ToLower()}, but get {lexer.GetLastValueType().ToString().ToLower()}"); + // May can be convert, so not return null + return new VariableDefineExprAST(valueType, isGlobal, name, lexer.GetLastValue()); + } + if (valueType == FieldType.Object) + { + string aqn = null; + string stringValue = token.Value; + // Allow two kinds of constraint define format + if (Regex.IsMatch(stringValue, FullAQNPattern)) + { + aqn = stringValue; + } + // Half and reverse + else if (Regex.IsMatch(stringValue, HalfAQNPattern)) + { + var items = stringValue.Split(','); + aqn = Assembly.CreateQualifiedName(items[0].Trim(), items[1].Trim()); + } + if (string.IsNullOrEmpty(aqn)) + //No aqn define + return new ObjectDefineExprAST(valueType, isGlobal, name, null, ValueExprAST.String(stringValue)); + //Move next to get actual value + return new ObjectDefineExprAST(valueType, isGlobal, name, aqn, ValueExprAST.String(lexer.GetNextToken().Value)); + } + //Identify as string, need visitor to read value. + return new VariableDefineExprAST(valueType, isGlobal, name, ValueExprAST.String(token.Value)); + } + private static void LogWarning(object message) + { + Debug.LogWarning($"[Parser] {message}"); + } + private static void LogError(object message) + { + Debug.LogError($"[Parser] {message}"); + } + } +} \ No newline at end of file diff --git a/Runtime/DSL/Parser.cs.meta b/Runtime/DSL/Parser.cs.meta new file mode 100644 index 0000000..009e69e --- /dev/null +++ b/Runtime/DSL/Parser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d44f34b296b3004daf12032be94d081 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Reader.cs b/Runtime/DSL/Reader.cs new file mode 100644 index 0000000..8bdf9e7 --- /dev/null +++ b/Runtime/DSL/Reader.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using UnityEngine; +using UnityEngine.Assertions; +namespace Kurisu.AkiBT.DSL +{ + public sealed class Reader + { + private const string Pattern = @"(\(|\)|\[|\,|\:|\]| |\n|\r|=>|\t)"; + private static readonly string[] ignoreTokens = new[] { "\n", "\r", "\t", " ", "" }; + private readonly string[] tokens; + public string CurrentToken + { + get + { + if (CurrentIndex < tokens.Length) return tokens[CurrentIndex]; + return null; + } + } + public int CurrentIndex { get; private set; } = 0; + public Reader(string[] tokens) + { + this.tokens = tokens; + } + public Reader(string stream) + { + tokens = Tokenize(stream); + } + /// + /// Read token, if not exist return null + /// + /// + public string Read() + { + int index = CurrentIndex++; + if (index < tokens.Length) + return tokens[index]; + return null; + } + public void MoveBack() + { + CurrentIndex--; + } + public void MoveNext() + { + CurrentIndex++; + } + public void MoveTo(int index) + { + CurrentIndex = index; + } + private static string[] Tokenize(string code) + { + int start = 0; + int flag = 0; + var tokens = new List(); + for (int i = 0; i < code.Length; ++i) + { + if (code[i] == '\"') + { + if (flag == 0) + { + tokens.AddRange(Regex.Split(code[start..i], Pattern)); + start = i + 1; + flag = 1; + } + else + { + tokens.Add(code[start..i]); + start = i + 1; + flag = 0; + } + } + } + if (start < code.Length) + tokens.AddRange(Regex.Split(code[(start - flag)..], Pattern)); + return tokens.Where(x => !ignoreTokens.Contains(x)).ToArray(); + } + public Vector3 ReadVector3() + { + float x, y, z; + AssertToken(Read(), Symbol.LeftParenthesis); + x = float.Parse(Read()); + AssertToken(Read(), Symbol.Comma); + y = float.Parse(Read()); + AssertToken(Read(), Symbol.Comma); + z = float.Parse(Read()); + AssertToken(Read(), Symbol.RightParenthesis); + return new Vector3(x, y, z); + } + public Vector3Int ReadVector3Int() + { + int x, y, z; + AssertToken(Read(), Symbol.LeftParenthesis); + x = int.Parse(Read()); + AssertToken(Read(), Symbol.Comma); + y = int.Parse(Read()); + AssertToken(Read(), Symbol.Comma); + z = int.Parse(Read()); + AssertToken(Read(), Symbol.RightParenthesis); + return new Vector3Int(x, y, z); + } + public Vector2 ReadVector2() + { + float x, y; + AssertToken(Read(), Symbol.LeftParenthesis); + x = float.Parse(Read()); + AssertToken(Read(), Symbol.Comma); + y = float.Parse(Read()); + AssertToken(Read(), Symbol.RightParenthesis); + return new Vector2(x, y); + } + public Vector2Int ReadVector2Int() + { + int x, y; + AssertToken(Read(), Symbol.LeftParenthesis); + x = int.Parse(Read()); + AssertToken(Read(), Symbol.Comma); + y = int.Parse(Read()); + AssertToken(Read(), Symbol.RightParenthesis); + return new Vector2Int(x, y); + } + private static void AssertToken(string token, string assertToken) + { + Assert.IsTrue(token == assertToken); + } + } +} \ No newline at end of file diff --git a/Runtime/DSL/Reader.cs.meta b/Runtime/DSL/Reader.cs.meta new file mode 100644 index 0000000..19f0b55 --- /dev/null +++ b/Runtime/DSL/Reader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 312278f9f3d4cde4c91db3e0c7371ac6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Symbol.cs b/Runtime/DSL/Symbol.cs new file mode 100644 index 0000000..6017578 --- /dev/null +++ b/Runtime/DSL/Symbol.cs @@ -0,0 +1,26 @@ +namespace Kurisu.AkiBT.DSL +{ + /// + /// Symbol table + /// + public static class Symbol + { + public const string Int = "Int"; + public const string Bool = "Bool"; + public const string Float = "Float"; + public const string String = "String"; + public const string Vector2 = "Vector2"; + public const string Vector2Int = "Vector2Int"; + public const string Vector3 = "Vector3"; + public const string Vector3Int = "Vector3Int"; + public const string Object = "Object"; + public const string LeftParenthesis = "("; + public const string RightParenthesis = ")"; + public const string LeftBracket = "["; + public const string RightBracket = "]"; + public const string Comma = ","; + public const string Colon = ":"; + public const string Shared = "=>"; + public const string Comment = "///"; + } +} \ No newline at end of file diff --git a/Runtime/DSL/Symbol.cs.meta b/Runtime/DSL/Symbol.cs.meta new file mode 100644 index 0000000..f401a85 --- /dev/null +++ b/Runtime/DSL/Symbol.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26a13b6d6a3974047a16e1bbe2ea3e6c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DSL/Token.cs b/Runtime/DSL/Token.cs new file mode 100644 index 0000000..0bd03f3 --- /dev/null +++ b/Runtime/DSL/Token.cs @@ -0,0 +1,87 @@ +using System; +namespace Kurisu.AkiBT.DSL +{ + public enum TokenType + { + // Dynamic token, need be interpreted by parser + DYNAMIC, + EOF, + // Code comment + COMMENT, + // Define sharedVariable + DEF_VARIABLE, + // Define node + DEF_NODE, + // Value type + VALUE + } + /// + /// Each token returned by the lexer includes a token type and potentially a string value + /// + public readonly struct Token + { + public TokenType Type { get; } + public string Value { get; } + public Token(TokenType tokenType) + { + Type = tokenType; + Value = default; + } + public Token(string value) + { + Type = TokenType.DYNAMIC; + Value = value; + } + public Token(TokenType tokenType, string value) + { + Type = tokenType; + Value = value; + } + public static bool operator ==(Token first, Token second) + { + return first.Type == second.Type && first.Value == second.Value; + } + + public static bool operator !=(Token first, Token second) + { + return first.Type != second.Type || first.Value != second.Value; + } + public static bool operator ==(Token first, TokenType second) + { + return first.Type == second; + } + + public static bool operator !=(Token first, TokenType second) + { + return first.Type != second; + } + public static bool operator ==(Token first, string second) + { + return first.Value == second; + } + + public static bool operator !=(Token first, string second) + { + return first.Value != second; + } + public bool Equals(Token other) + { + return other.Type == Type && other.Value == Value; + } + + public override bool Equals(object obj) + { + return obj is Token token && Equals(token); + } + + public override int GetHashCode() + { + return HashCode.Combine(Type, Value); + } + + public override string ToString() + { + return $"[{Type}]{Value}"; + } + } +} diff --git a/Runtime/DSL/Token.cs.meta b/Runtime/DSL/Token.cs.meta new file mode 100644 index 0000000..b5bc16a --- /dev/null +++ b/Runtime/DSL/Token.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d3a79ed312ad77445b29df571b7edfcb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/AkiBTCode.txt b/Samples~/DSL Example/AkiBTCode.txt new file mode 100644 index 0000000..fc1279d --- /dev/null +++ b/Samples~/DSL Example/AkiBTCode.txt @@ -0,0 +1,33 @@ +/// +Behavior tree written by DSL +/// +Vector3 destination (0,0,0) +Vector3 myPos (0,0,0) +Float distance 1 +Vector3 subtract (0,0,0) +Parallel(children:[ + Sequence(children:[ + Vector3Random(xRange:(-10,10),yRange:(0,0),zRange:(-10,10),operation:Relatively, + storeResult=>destination ), + DebugLog(logText:"Agent get a new destination!"), + TimeWait(waitTime:10) + ]), + Sequence(children:[ + Sequence(children:[ + TransformGetPosition(target:null,storeResult=>myPos), + Vector3Operator(operation:Subtract,firstVector3=>myPos, + secondVector3=>destination,storeResult=>subtract), + Vector3GetSqrMagnitude(vector3=>subtract,result=>distance) + ]), + Selector(abortOnConditionChanged: false, children:[ + FloatComparison(evaluateOnRunning:false,float1=>distance, + float2:4,operation:GreaterThan,child: + Sequence(abortOnConditionChanged:false,children:[ + NavmeshStopAgent(agent:null,isStopped:false), + NavmeshSetDestination(agent:null,destination=>destination) + ]) + ), + NavmeshStopAgent(agent:null,isStopped:true) + ]) + ]) +]) diff --git a/Samples~/DSL Example/AkiBTCode.txt.meta b/Samples~/DSL Example/AkiBTCode.txt.meta new file mode 100644 index 0000000..e2dc0a1 --- /dev/null +++ b/Samples~/DSL Example/AkiBTCode.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fa901c56198255e41acf55f68a5cbed9 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/Materials.meta b/Samples~/DSL Example/Materials.meta new file mode 100644 index 0000000..b0a78e8 --- /dev/null +++ b/Samples~/DSL Example/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 61d3e8469cc864e4eb07a7945f64dac0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/Materials/Ground.mat b/Samples~/DSL Example/Materials/Ground.mat new file mode 100644 index 0000000..fe4fe56 --- /dev/null +++ b/Samples~/DSL Example/Materials/Ground.mat @@ -0,0 +1,80 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Ground + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 10, y: 10} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 10309, guid: 0000000000000000f000000000000000, type: 0} + m_Scale: {x: 10, y: 10} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Samples~/DSL Example/Materials/Ground.mat.meta b/Samples~/DSL Example/Materials/Ground.mat.meta new file mode 100644 index 0000000..b1c7a61 --- /dev/null +++ b/Samples~/DSL Example/Materials/Ground.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0489b22f3454b97499132fedc799deec +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/Materials/Player.mat b/Samples~/DSL Example/Materials/Player.mat new file mode 100644 index 0000000..d63c072 --- /dev/null +++ b/Samples~/DSL Example/Materials/Player.mat @@ -0,0 +1,80 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Player + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 10, y: 10} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 10, y: 10} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0, g: 0.9167206, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Samples~/DSL Example/Materials/Player.mat.meta b/Samples~/DSL Example/Materials/Player.mat.meta new file mode 100644 index 0000000..912a22e --- /dev/null +++ b/Samples~/DSL Example/Materials/Player.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 34a94f44e4911424d8ddd6c42095d889 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/Patrol.asset b/Samples~/DSL Example/Patrol.asset new file mode 100644 index 0000000..7fb4922 --- /dev/null +++ b/Samples~/DSL Example/Patrol.asset @@ -0,0 +1,408 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 77eff6e90efa77848b31d5d488608422, type: 3} + m_Name: Patrol + m_EditorClassIdentifier: + root: + rid: 7861896391660142592 + blockData: + - ChildNodes: + - 96af5d7e-4a4c-4adf-ade4-ebdb5680d378 + - ae2413f2-7d22-4bf6-887d-56a800991790 + - c88d247b-2381-46fb-a3e2-3492282f8b92 + - a76dcebd-fe44-4e44-b694-18f5f22803bf + - ba145914-2040-49ed-b629-bb9f83883864 + - 08773f34-7726-4e8c-9873-6446e7daa2ac + - 47798a6a-20ed-420f-af06-ee03dbf79877 + - fe203ab5-ae5d-41fa-b9e1-af6ff5de22ee + - 1489e59a-e6dc-4c32-ab7d-0591ec84d33e + - 92ca3ea0-da01-4b82-ab7d-51e8d7e92bf5 + - daf0f0cf-eaa6-4779-b663-a11c2b129d90 + - e0b4dbaa-18b3-4d68-adc2-3972f3dd18fc + - 337eb0ec-2673-464d-8b58-159df0c63452 + - addd75b7-30b3-4635-bded-8ef5462de6eb + - f432487b-bb7c-4697-8046-fadcb1c4f619 + - c8638184-5e92-4e64-856f-dbae9099a2ff + Position: {x: 505.99997, y: -493.99997} + Title: Per 10 seconds, find a new destination + Description: Original BehaviorTree created by AkiBT editor + sharedVariables: + - rid: 7861896391660142593 + - rid: 7861896391660142594 + - rid: 7861896391660142595 + - rid: 7861896391660142596 + references: + version: 2 + RefIds: + - rid: 7861896391660142592 + type: {class: Root, ns: Kurisu.AkiBT, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 400 + y: 300 + width: 80.66666 + height: 78.66669 + description: + guid: c9aa6048-669b-4d52-9191-77a7a84fa694 + child: + rid: 7861896391660142597 + - rid: 7861896391660142593 + type: {class: SharedVector3, ns: Kurisu.AkiBT, asm: Kurisu.AkiBT} + data: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: destination + value: {x: 0, y: 0, z: 0} + - rid: 7861896391660142594 + type: {class: SharedVector3, ns: Kurisu.AkiBT, asm: Kurisu.AkiBT} + data: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: myPos + value: {x: 0, y: 0, z: 0} + - rid: 7861896391660142595 + type: {class: SharedFloat, ns: Kurisu.AkiBT, asm: Kurisu.AkiBT} + data: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: distance + value: 1 + - rid: 7861896391660142596 + type: {class: SharedVector3, ns: Kurisu.AkiBT, asm: Kurisu.AkiBT} + data: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: subtract + value: {x: 0, y: 0, z: 0} + - rid: 7861896391660142597 + type: {class: Parallel, ns: Kurisu.AkiBT, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 531.3333 + y: 280 + width: 136 + height: 122.66669 + description: + guid: 96af5d7e-4a4c-4adf-ade4-ebdb5680d378 + children: + - rid: 7861896391660142598 + - rid: 7861896391660142599 + - rid: 7861896391660142598 + type: {class: Sequence, ns: Kurisu.AkiBT, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 716 + y: -224 + width: 175.33331 + height: 165.33333 + description: + guid: 337eb0ec-2673-464d-8b58-159df0c63452 + children: + - rid: 7861896391660142600 + - rid: 7861896391660142601 + - rid: 7861896391660142602 + abortOnConditionChanged: 0 + - rid: 7861896391660142599 + type: {class: Sequence, ns: Kurisu.AkiBT, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 716 + y: 766 + width: 175.33331 + height: 141.33331 + description: + guid: ae2413f2-7d22-4bf6-887d-56a800991790 + children: + - rid: 7861896391660142603 + - rid: 7861896391660142604 + abortOnConditionChanged: 0 + - rid: 7861896391660142600 + type: {class: Vector3Random, ns: Kurisu.AkiBT.Extend, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 939.3333 + y: -435.3333 + width: 220.66669 + height: 221.33333 + description: "\u83B7\u53D6\u5F53\u524D\u4F4D\u7F6E\u8303\u56F4\u5185\u968F\u673A\u4F4D\u7F6E" + guid: c8638184-5e92-4e64-856f-dbae9099a2ff + xRange: {x: -10, y: 10} + yRange: {x: 0, y: 0} + zRange: {x: -10, y: 10} + operation: 1 + storeResult: + isShared: 1 + isGlobal: 0 + isExposed: 0 + mName: destination + value: {x: 0, y: 0, z: 0} + - rid: 7861896391660142601 + type: {class: DebugLog, ns: Kurisu.AkiBT.Extend, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 939.3333 + y: -170 + width: 214.66669 + height: 139.33334 + description: "Patrol\u83B7\u53D6\u4E86\u65B0\u4F4D\u7F6E" + guid: f432487b-bb7c-4697-8046-fadcb1c4f619 + logText: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: + value: "Patrol\u83B7\u53D6\u4E86\u65B0\u4F4D\u7F6E" + - rid: 7861896391660142602 + type: {class: TimeWait, ns: Kurisu.AkiBT.Extend, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 939.3333 + y: 14.666664 + width: 189.33331 + height: 138 + description: "10\u79D2" + guid: addd75b7-30b3-4635-bded-8ef5462de6eb + waitTime: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: + value: 10 + - rid: 7861896391660142603 + type: {class: Sequence, ns: Kurisu.AkiBT, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 939.3333 + y: 456.6667 + width: 175.33331 + height: 166.66666 + description: "\u8BA1\u7B97\u5E8F\u5217" + guid: 1489e59a-e6dc-4c32-ab7d-0591ec84d33e + children: + - rid: 7861896391660142605 + - rid: 7861896391660142606 + - rid: 7861896391660142607 + abortOnConditionChanged: 0 + - rid: 7861896391660142604 + type: {class: Selector, ns: Kurisu.AkiBT, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 939.3333 + y: 1074 + width: 175.33331 + height: 141.33337 + description: + guid: c88d247b-2381-46fb-a3e2-3492282f8b92 + children: + - rid: 7861896391660142608 + - rid: 7861896391660142609 + abortOnConditionChanged: 0 + - rid: 7861896391660142605 + type: {class: TransformGetPosition, ns: Kurisu.AkiBT.Extend, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 1162 + y: 200 + width: 240.66663 + height: 177.33333 + description: + guid: e0b4dbaa-18b3-4d68-adc2-3972f3dd18fc + target: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: + value: {fileID: 0} + storeResult: + isShared: 1 + isGlobal: 0 + isExposed: 0 + mName: myPos + value: {x: 0, y: 0, z: 0} + - rid: 7861896391660142606 + type: {class: Vector3Operator, ns: Kurisu.AkiBT.Extend, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 1162 + y: 422.6667 + width: 273.33337 + height: 239.33334 + description: "\u4F4D\u7F6E\u76F8\u51CF\u5B58\u5230subtract\u4E2D" + guid: daf0f0cf-eaa6-4779-b663-a11c2b129d90 + operation: 1 + firstVector3: + isShared: 1 + isGlobal: 0 + isExposed: 0 + mName: myPos + value: {x: 0, y: 0, z: 0} + secondVector3: + isShared: 1 + isGlobal: 0 + isExposed: 0 + mName: destination + value: {x: 0, y: 0, z: 0} + storeResult: + isShared: 1 + isGlobal: 0 + isExposed: 0 + mName: subtract + value: {x: 0, y: 0, z: 0} + - rid: 7861896391660142607 + type: {class: Vector3GetSqrMagnitude, ns: Kurisu.AkiBT.Extend, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 1162 + y: 704 + width: 259.33337 + height: 178.66663 + description: "\u83B7\u53D6subtract\u6A21\u7684\u5E73\u65B9\u5B58\u5230distance" + guid: 92ca3ea0-da01-4b82-ab7d-51e8d7e92bf5 + vector3: + isShared: 1 + isGlobal: 0 + isExposed: 0 + mName: subtract + value: {x: 0, y: 0, z: 0} + result: + isShared: 1 + isGlobal: 0 + isExposed: 0 + mName: distance + value: 0 + - rid: 7861896391660142608 + type: {class: FloatComparison, ns: Kurisu.AkiBT.Extend, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 1162 + y: 926.6666 + width: 244 + height: 217.33331 + description: "\u5224\u65ADdistance>4\u5373\u8DDD\u79BB>2" + guid: ba145914-2040-49ed-b629-bb9f83883864 + evaluateOnRunning: 0 + child: + rid: 7861896391660142610 + float1: + isShared: 1 + isGlobal: 0 + isExposed: 0 + mName: distance + value: 0 + float2: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: + value: 4 + operation: 5 + - rid: 7861896391660142609 + type: {class: NavmeshStopAgent, ns: Kurisu.AkiBT.Extend, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 1162 + y: 1188 + width: 273.33337 + height: 176.66675 + description: "\u505C\u6B62\u79FB\u52A8" + guid: a76dcebd-fe44-4e44-b694-18f5f22803bf + agent: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: + value: {fileID: 0} + isStopped: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: + value: 1 + - rid: 7861896391660142610 + type: {class: Sequence, ns: Kurisu.AkiBT, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 1451.3334 + y: 962.6666 + width: 175.33325 + height: 143.33331 + description: "\u79FB\u52A8\u5E8F\u5217" + guid: 08773f34-7726-4e8c-9873-6446e7daa2ac + children: + - rid: 7861896391660142611 + - rid: 7861896391660142612 + abortOnConditionChanged: 0 + - rid: 7861896391660142611 + type: {class: NavmeshStopAgent, ns: Kurisu.AkiBT.Extend, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 1674 + y: 836 + width: 273.33337 + height: 176.66669 + description: "\u5173\u95ED\u505C\u6B62" + guid: fe203ab5-ae5d-41fa-b9e1-af6ff5de22ee + agent: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: + value: {fileID: 0} + isStopped: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: + value: 0 + - rid: 7861896391660142612 + type: {class: NavmeshSetDestination, ns: Kurisu.AkiBT.Extend, asm: Kurisu.AkiBT} + data: + graphPosition: + serializedVersion: 2 + x: 1674 + y: 1056.6667 + width: 273.33337 + height: 179.33325 + description: "\u79FB\u52A8\u5230destination" + guid: 47798a6a-20ed-420f-af06-ee03dbf79877 + agent: + isShared: 0 + isGlobal: 0 + isExposed: 0 + mName: + value: {fileID: 0} + destination: + isShared: 1 + isGlobal: 0 + isExposed: 0 + mName: destination + value: {x: 0, y: 0, z: 0} diff --git a/Samples~/DSL Example/Patrol.asset.meta b/Samples~/DSL Example/Patrol.asset.meta new file mode 100644 index 0000000..260e40a --- /dev/null +++ b/Samples~/DSL Example/Patrol.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 38b9a98ca466fc6489f73bb6ed9d951b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/Scripts.meta b/Samples~/DSL Example/Scripts.meta new file mode 100644 index 0000000..2832213 --- /dev/null +++ b/Samples~/DSL Example/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 36b620effe30d104f825e150c5cfcf84 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/Scripts/Editor.meta b/Samples~/DSL Example/Scripts/Editor.meta new file mode 100644 index 0000000..1b85981 --- /dev/null +++ b/Samples~/DSL Example/Scripts/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f9626995a93e2af4aabd94ad9d279825 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/Scripts/Editor/BehaviorTreeVMEditor.cs b/Samples~/DSL Example/Scripts/Editor/BehaviorTreeVMEditor.cs new file mode 100644 index 0000000..00713e2 --- /dev/null +++ b/Samples~/DSL Example/Scripts/Editor/BehaviorTreeVMEditor.cs @@ -0,0 +1,109 @@ +using UnityEditor; +using UnityEngine.UIElements; +using UnityEditor.UIElements; +using System; +using UnityEngine; +namespace Kurisu.AkiBT.Example.Editor +{ + [CustomEditor(typeof(BehaviorTreeVM))] + public class BehaviorTreeVMEditor : UnityEditor.Editor + { + private Button createButton; + private Button clearButton; + private Button runButton; + private Button stopButton; + private BehaviorTreeVM vm; + private ObjectField codeAssetField; + const string LabelText = "AkiBTVM"; + public override VisualElement CreateInspectorGUI() + { + vm = target as BehaviorTreeVM; + VisualElement inspectorRoot = new(); + inspectorRoot.Add(VMEditorUtility.GetTitleLabel(LabelText)); + + //Toggle + var toggle = new Toggle("Is Playing") + { + bindingPath = "isPlaying" + }; + toggle.SetEnabled(false); + inspectorRoot.Add(toggle); + //Input + inspectorRoot.Add(codeAssetField = new ObjectField("Input Code") { objectType = typeof(TextAsset) }); + inspectorRoot.Add(new ObjectField("Output BehaviorTreeSO") { bindingPath = "behaviorTreeSO", objectType = typeof(BehaviorTreeSO) }); + //group + var group1 = VMEditorUtility.GetGroup(); + var group2 = VMEditorUtility.GetGroup(); + //Button + createButton = VMEditorUtility.GetButton("Compile", callBack: () => EditorApplication.update += Compile, widthPercent: 100); + inspectorRoot.Add(createButton); + + var saveButton = VMEditorUtility.GetButton("Save", callBack: Save); + group1.Add(saveButton); + + clearButton = VMEditorUtility.GetButton("Clear", callBack: vm.Clear); + group1.Add(clearButton); + inspectorRoot.Add(group1); + + runButton = VMEditorUtility.GetButton("Run", new Color(140 / 255f, 160 / 255f, 250 / 255f), vm.Run); + group2.Add(runButton); + + stopButton = VMEditorUtility.GetButton("Stop", new Color(253 / 255f, 163 / 255f, 255 / 255f), vm.Stop); + group2.Add(stopButton); + inspectorRoot.Add(group2); + + return inspectorRoot; + } + private void Save() + { + if (vm.BehaviorTreeSO == null) + { + Debug.Log("AkiBTVM:VMBehaviorTreeSO Hasn't Created Yet!"); + return; + } + string path = EditorUtility.OpenFolderPanel("Select saving path", Application.dataPath, ""); + if (string.IsNullOrEmpty(path)) return; + var savePath = path.Replace(Application.dataPath, string.Empty); + string outPutPath = $"Assets/{savePath}/{vm.gameObject.name}.asset"; + AssetDatabase.CreateAsset(vm.BehaviorTreeSO, outPutPath); + AssetDatabase.SaveAssets(); + Debug.Log($"AkiBTVM:BehaviorTreeSO saved succeed! File Path:{outPutPath}"); + } + private void Compile() + { + EditorApplication.update -= Compile; + var vm = target as BehaviorTreeVM; + if (codeAssetField.value != null) + { + vm.Compile((codeAssetField.value as TextAsset).text); + } + } + } +} +internal class VMEditorUtility +{ + internal static Button GetButton(string text, Color? color = null, System.Action callBack = null, float widthPercent = 50) + { + var button = new Button(); + if (callBack != null) button.clicked += callBack; + if (color.HasValue) button.style.backgroundColor = color.Value; + button.style.width = Length.Percent(widthPercent); + button.text = text; + button.style.fontSize = 15; + return button; + } + internal static VisualElement GetGroup() + { + var group = new VisualElement(); + group.style.flexDirection = FlexDirection.Row; + return group; + } + internal static Label GetTitleLabel(string text, int frontSize = 20) + { + var label = new Label(text); + label.style.fontSize = frontSize; + label.style.unityTextAlign = TextAnchor.MiddleCenter; + return label; + } +} + diff --git a/Samples~/DSL Example/Scripts/Editor/BehaviorTreeVMEditor.cs.meta b/Samples~/DSL Example/Scripts/Editor/BehaviorTreeVMEditor.cs.meta new file mode 100644 index 0000000..a344e66 --- /dev/null +++ b/Samples~/DSL Example/Scripts/Editor/BehaviorTreeVMEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8069911b48e8e044f9f625114a391177 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/Scripts/Editor/Kurisu.AkiBT.DSL.Example.Editor.asmdef b/Samples~/DSL Example/Scripts/Editor/Kurisu.AkiBT.DSL.Example.Editor.asmdef new file mode 100644 index 0000000..79d8150 --- /dev/null +++ b/Samples~/DSL Example/Scripts/Editor/Kurisu.AkiBT.DSL.Example.Editor.asmdef @@ -0,0 +1,19 @@ +{ + "name": "Kurisu.AkiBT.DSL.Example.Editor", + "rootNamespace": "Kurisu.AkiBT.DSL.Example.Editor", + "references": [ + "GUID:9c5c13d5e730dcd4a8441078c6524b89", + "GUID:bf89a8bf6625fad4eb6879c01e73541d" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Samples~/DSL Example/Scripts/Editor/Kurisu.AkiBT.DSL.Example.Editor.asmdef.meta b/Samples~/DSL Example/Scripts/Editor/Kurisu.AkiBT.DSL.Example.Editor.asmdef.meta new file mode 100644 index 0000000..a75a9d4 --- /dev/null +++ b/Samples~/DSL Example/Scripts/Editor/Kurisu.AkiBT.DSL.Example.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 24e3e0275f12ca14a9e55a2417b8d73c +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/Scripts/Runtime.meta b/Samples~/DSL Example/Scripts/Runtime.meta new file mode 100644 index 0000000..f95e0ac --- /dev/null +++ b/Samples~/DSL Example/Scripts/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 414312b514d9a2b40bc8fff1bdd856cb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/Scripts/Runtime/BehaviorTreeVM.cs b/Samples~/DSL Example/Scripts/Runtime/BehaviorTreeVM.cs new file mode 100644 index 0000000..4002ec9 --- /dev/null +++ b/Samples~/DSL Example/Scripts/Runtime/BehaviorTreeVM.cs @@ -0,0 +1,54 @@ +using UnityEngine; +using Kurisu.AkiBT.DSL; +using System.IO; +namespace Kurisu.AkiBT.Example +{ + public class BehaviorTreeVM : MonoBehaviour + { + private Compiler compiler; + [SerializeField] + private bool isPlaying; + public bool IsPlaying => isPlaying; + [SerializeField] + private BehaviorTreeSO behaviorTreeSO; + public BehaviorTreeSO BehaviorTreeSO => behaviorTreeSO; + private void Start() + { + compiler = new Compiler(Path.Combine(Application.streamingAssetsPath, "NodeTypeRegistry.json")); + } + /// + /// Compile vmCode. if success, you will have a temporary BehaviorTreeSO inside this component. + /// You can save this BehaviorTreeSO in the editor by clicking 'Save' button + /// + /// + public void Compile(string vmCode) + { + if (behaviorTreeSO != null) Clear(); + behaviorTreeSO = compiler.Verbose(true).Compile(vmCode); + } + public void Clear() + { + behaviorTreeSO = null; + isPlaying = false; + } + /// + /// VMBehaviorTreeSO doesn't automatically awake and start, you need to run it manually + /// + public void Run() + { + if (behaviorTreeSO == null || isPlaying) return; + behaviorTreeSO.Initialize(); + behaviorTreeSO.Init(gameObject); + isPlaying = true; + } + public void Stop() + { + isPlaying = false; + } + private void Update() + { + if (isPlaying) behaviorTreeSO.Update(); + } + + } +} diff --git a/Samples~/DSL Example/Scripts/Runtime/BehaviorTreeVM.cs.meta b/Samples~/DSL Example/Scripts/Runtime/BehaviorTreeVM.cs.meta new file mode 100644 index 0000000..0c79997 --- /dev/null +++ b/Samples~/DSL Example/Scripts/Runtime/BehaviorTreeVM.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61094eeedf4cba64caf7d037be5075ee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/Scripts/Runtime/Kurisu.AkiBT.DSL.Example.asmdef b/Samples~/DSL Example/Scripts/Runtime/Kurisu.AkiBT.DSL.Example.asmdef new file mode 100644 index 0000000..ba69538 --- /dev/null +++ b/Samples~/DSL Example/Scripts/Runtime/Kurisu.AkiBT.DSL.Example.asmdef @@ -0,0 +1,17 @@ +{ + "name": "Kurisu.AkiBT.DSL.Example", + "rootNamespace": "Kurisu.AkiBT.DSL.Example", + "references": [ + "GUID:bf89a8bf6625fad4eb6879c01e73541d", + "GUID:ea13d7331a0df234f85e126949843072" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Samples~/DSL Example/Scripts/Runtime/Kurisu.AkiBT.DSL.Example.asmdef.meta b/Samples~/DSL Example/Scripts/Runtime/Kurisu.AkiBT.DSL.Example.asmdef.meta new file mode 100644 index 0000000..b45133d --- /dev/null +++ b/Samples~/DSL Example/Scripts/Runtime/Kurisu.AkiBT.DSL.Example.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9c5c13d5e730dcd4a8441078c6524b89 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/VMExample.meta b/Samples~/DSL Example/VMExample.meta new file mode 100644 index 0000000..0f6160b --- /dev/null +++ b/Samples~/DSL Example/VMExample.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e8b88f9c57ce98842bd73d130fcf941a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/VMExample.unity b/Samples~/DSL Example/VMExample.unity new file mode 100644 index 0000000..7ea794d --- /dev/null +++ b/Samples~/DSL Example/VMExample.unity @@ -0,0 +1,895 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.18028378, g: 0.22571412, b: 0.30692285, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 23800000, guid: 6543bbb81a865a1419d7acb9f8b3063b, type: 2} +--- !u!1 &275806729 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 275806730} + m_Layer: 0 + m_Name: 1. Enter play mode + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &275806730 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 275806729} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -8.960014, y: 2.9287028, z: 0.8125901} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &320041470 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 320041476} + - component: {fileID: 320041475} + - component: {fileID: 320041474} + - component: {fileID: 320041473} + - component: {fileID: 320041472} + - component: {fileID: 320041471} + m_Layer: 0 + m_Name: PatrolAI + m_TagString: Untagged + m_Icon: {fileID: -964228994112308473, guid: 0000000000000000d000000000000000, type: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &320041471 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 320041470} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61094eeedf4cba64caf7d037be5075ee, type: 3} + m_Name: + m_EditorClassIdentifier: + isPlaying: 0 + behaviorTreeSO: {fileID: 0} +--- !u!195 &320041472 +NavMeshAgent: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 320041470} + m_Enabled: 1 + m_AgentTypeID: 0 + m_Radius: 0.5 + m_Speed: 3.5 + m_Acceleration: 8 + avoidancePriority: 50 + m_AngularSpeed: 120 + m_StoppingDistance: 0 + m_AutoTraverseOffMeshLink: 1 + m_AutoBraking: 1 + m_AutoRepath: 1 + m_Height: 2 + m_BaseOffset: 1 + m_WalkableMask: 4294967295 + m_ObstacleAvoidanceType: 4 +--- !u!136 &320041473 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 320041470} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &320041474 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 320041470} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 34a94f44e4911424d8ddd6c42095d889, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &320041475 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 320041470} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &320041476 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 320041470} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -0.41, y: 1, z: 7.68} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &337025081 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 337025082} + m_Layer: 0 + m_Name: 3. If compile succeed, click run + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &337025082 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 337025081} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -8.960014, y: 2.9287028, z: 0.8125901} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &488310074 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 488310076} + - component: {fileID: 488310075} + - component: {fileID: 488310077} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &488310075 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 488310074} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &488310076 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 488310074} + serializedVersion: 2 + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0.81424904, y: 8.287397, z: -6.332284} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1953241686} + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!114 &488310077 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 488310074} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Version: 3 + m_UsePipelineSettings: 1 + m_AdditionalLightsShadowResolutionTier: 2 + m_LightLayerMask: 1 + m_RenderingLayers: 1 + m_CustomShadowLayers: 0 + m_ShadowLayerMask: 1 + m_ShadowRenderingLayers: 1 + m_LightCookieSize: {x: 1, y: 1} + m_LightCookieOffset: {x: 0, y: 0} + m_SoftShadowQuality: 1 +--- !u!1 &941161013 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 941161014} + m_Layer: 0 + m_Name: '=======AI======== ' + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &941161014 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 941161013} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -0.81424904, y: -5.287398, z: 6.332284} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1379515258 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1379515259} + m_Layer: 0 + m_Name: 2. Drag code text to vm and click compile + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1379515259 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1379515258} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -8.960014, y: 2.9287028, z: 0.8125901} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1576608359 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1576608360} + m_Layer: 0 + m_Name: This is a patrol ai written in DSL + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1576608360 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1576608359} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -8.960014, y: 2.9287028, z: 0.8125901} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1676695616 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1676695617} + m_Layer: 0 + m_Name: =======Introduction======== + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1676695617 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1676695616} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -0.81424904, y: -5.287398, z: 6.332284} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1953241685 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1953241686} + m_Layer: 0 + m_Name: ===BackGround=== + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1953241686 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1953241685} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -0.81424904, y: -5.287398, z: 6.332284} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2130379003} + - {fileID: 488310076} + - {fileID: 1969741449} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1969741445 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1969741449} + - component: {fileID: 1969741448} + - component: {fileID: 1969741447} + - component: {fileID: 1969741446} + - component: {fileID: 1969741450} + m_Layer: 0 + m_Name: Ground + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 2147483647 + m_IsActive: 1 +--- !u!64 &1969741446 +MeshCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1969741445} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 5 + m_Convex: 0 + m_CookingOptions: 30 + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1969741447 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1969741445} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0489b22f3454b97499132fedc799deec, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1969741448 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1969741445} + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1969741449 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1969741445} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.81424904, y: 5.287398, z: -6.332284} + m_LocalScale: {x: 10, y: 1, z: 10} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1953241686} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1969741450 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1969741445} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7a5ac11cc976e418e8d13136b07e1f52, type: 3} + m_Name: + m_EditorClassIdentifier: + m_AgentTypeID: 0 + m_CollectObjects: 0 + m_Size: {x: 10, y: 10, z: 10} + m_Center: {x: 0, y: 2, z: 0} + m_LayerMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_UseGeometry: 0 + m_DefaultArea: 0 + m_GenerateLinks: 0 + m_IgnoreNavMeshAgent: 1 + m_IgnoreNavMeshObstacle: 1 + m_OverrideTileSize: 0 + m_TileSize: 256 + m_OverrideVoxelSize: 0 + m_VoxelSize: 0.16666667 + m_MinRegionArea: 2 + m_NavMeshData: {fileID: 23800000, guid: eee9e970da708dc4abf40a356f599803, type: 2} + m_BuildHeightMesh: 0 +--- !u!1 &2130379000 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2130379003} + - component: {fileID: 2130379002} + - component: {fileID: 2130379001} + - component: {fileID: 2130379004} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &2130379001 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2130379000} + m_Enabled: 1 +--- !u!20 &2130379002 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2130379000} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &2130379003 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2130379000} + serializedVersion: 2 + m_LocalRotation: {x: 0.41937286, y: 0, z: 0, w: 0.9078141} + m_LocalPosition: {x: 0.81424904, y: 24.1, z: -13.902285} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1953241686} + m_LocalEulerAnglesHint: {x: 49.59, y: 0, z: 0} +--- !u!114 &2130379004 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2130379000} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_VolumeFrameworkUpdateModeOption: 2 + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_AllowHDROutput: 1 + m_UseScreenCoordOverride: 0 + m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0} + m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0} + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 + m_TaaSettings: + quality: 3 + frameInfluence: 0.1 + jitterScale: 1 + mipBias: 0 + varianceClampScale: 0.9 + contrastAdaptiveSharpening: 0 +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 1953241686} + - {fileID: 941161014} + - {fileID: 320041476} + - {fileID: 1676695617} + - {fileID: 1576608360} + - {fileID: 275806730} + - {fileID: 1379515259} + - {fileID: 337025082} diff --git a/Samples~/DSL Example/VMExample.unity.meta b/Samples~/DSL Example/VMExample.unity.meta new file mode 100644 index 0000000..c714444 --- /dev/null +++ b/Samples~/DSL Example/VMExample.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b89096a9cb8f1024a851e8fda588a486 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/DSL Example/VMExample/NavMesh-Ground.asset b/Samples~/DSL Example/VMExample/NavMesh-Ground.asset new file mode 100644 index 0000000..1e507e3 Binary files /dev/null and b/Samples~/DSL Example/VMExample/NavMesh-Ground.asset differ diff --git a/Samples~/DSL Example/VMExample/NavMesh-Ground.asset.meta b/Samples~/DSL Example/VMExample/NavMesh-Ground.asset.meta new file mode 100644 index 0000000..5274a3b --- /dev/null +++ b/Samples~/DSL Example/VMExample/NavMesh-Ground.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eee9e970da708dc4abf40a356f599803 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 23800000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/package.json b/package.json index 37801ee..f6bc1a4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.kurisu.akibt", "displayName": "AkiBT", - "version": "1.4.8", + "version": "1.5.0", "unity": "2021.3", "description": "Node based Behavior Tree Editor.\nSupports visualizing active node in runtime.\nEasily add original behaviors.", "keywords": [ @@ -32,6 +32,11 @@ "displayName": "Builder Example", "description": "Contains builder sample for BehaviorTreeBuilder", "path": "Samples~/Builder Example" + }, + { + "displayName": "DSL Example", + "description": "Contains builder sample for AkiBT domain specific language", + "path": "Samples~/DSL Example" } ] } \ No newline at end of file