public void DynamicTestScript() { var config = new YggParserConfig(); config.ReplaceObjectStateWithDynamic = true; config.ScriptNamespaces.Add(typeof(Result).Namespace); config.ReferenceAssemblyPaths.Add(typeof(Node).Assembly.Location); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(TestDynamicFunction).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var context = parser.BuildFromFiles <object>("ParserTests\\testScriptC.ygg"); Assert.AreEqual(0, context.Errors.Count); Assert.AreEqual(1, context.TopmostNodeCount); var root = context.Instantiate("root"); Assert.IsNotNull(root); dynamic state = new ExpandoObject(); var manager = new BehaviourTree(root); manager.Update(state); Assert.AreEqual(1UL, manager.TickCount); Assert.AreEqual(Result.Success, manager.Result); Assert.AreEqual(1, state.A); }
public void TestScriptB() { var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(TestRunningAction).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var context = parser.BuildFromFiles <TestState>("ParserTests\\testScriptB.ygg"); Assert.AreEqual(0, context.Errors.Count); Assert.AreEqual(3, context.TopmostNodeCount); var root = context.Instantiate("root"); Assert.IsNotNull(root); var manager = new BehaviourTree(root); AssertScriptB(manager); manager.Reset(); AssertScriptB(manager); }
public void Setup() { var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(TestRunningAction).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var scriptPath = Path.Combine(Environment.CurrentDirectory, "BenchmarkScripts\\testScriptB.ygg"); var context = parser.BuildFromFiles <TestState>(scriptPath); if (context.Errors.Count > 0) { throw new Exception(context.Errors[0].Message); } _tree = new BehaviourTree(context.Instantiate("root")); _state = new TestState(); // Warmup. while (_tree.TickCount == 0) { _tree.Update(_state); } _state = new TestState(); }
public void Start() { // You need to pass the "netstandard" assembly reference when in Unity. // This is not necessary on a standalone .NET Core 2.2 console program. // You can also pass more references and namespace usings for the C# snippets if needed. var netstandard = AppDomain.CurrentDomain.GetAssemblies().First(n => n.GetName().Name == "netstandard").Location; var config = new YggParserConfig(); config.ReferenceAssemblyPaths.Add(netstandard); // You choose which currently loaded assemblies are searched for types deriving from the Node class. config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(ExampleCustomAction).Assembly.GetName().Name); // Create the compiler and parser. They can be reused for multiple parsings. // The only state maintained by a parser instance is the initial config passed on its constructor. var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); // The parser gives back a behaviour tree definition, which can be used to instantiate the tree multiple times (or any node within by its GUID). // The definition also keeps track of errors that might have happened during parsing, and other debug information. // Node GUIDs can be assigned manually on the script files themselves. If they don't have one, the parser assigns a random one. They must be unique // withing the context of all the files parsed. The same goes for 'TypeDefs'. You can reference TypeDefs between files. var scriptFilePath = Path.Combine(Application.streamingAssetsPath, "exampleScript.ygg"); var definition = parser.BuildFromFiles <ExampleState>(scriptFilePath); // The errors often have useful information to debug them. In this example there should be none. if (definition.Errors.Count > 0) { throw new Exception("Errors while parsing script files."); } // You can instantiate any node defined on the parsed scripts by passing their GUID. // This creates an instance of that node and all its descendants. // This also calls Initialize() on the instantiated nodes (in a depth-first traversal, if that matters to you). // The script files themselves have no explicit root, so you must instantiate the tree using the desired root node's GUID. var root = definition.Instantiate("root"); // The behaviour tree object serves as a manager that handles the node's execution and async continuations. // The behaviour tree is what keeps the "continuation state" of the tree. This means you can reuse a single "root" node instance // on multiple behaviour trees that get updated separately without issues. This is only valid for the node types that Yggdrasil comes // with by default. If you create new node types inheriting from the Node class, you must make sure they don't keep any state themselves. _tree = new BehaviourTree(root); // Some example state object passed to the tree's nodes. Kept as a field here to allocate it only once. _state = new ExampleState(); // You can pre-pool some coroutines. Otherwise, the first run of the tree will allocate as necessary. // They get recycled automatically. The amount of coroutines needed depends on how deep an async Coroutine method stack call can get during execution. // Unless the nodes use multiple nested async Coroutine methods, this means only one Coroutine<Result> per node on the deepest possible branch of the tree. // Parallel and Interrupt nodes can complicate the math, since they create multiple simultaneously active branches. // Pre-pooling yourself like this is optional. It'll create as necessary during execution. Yggdrasil.Coroutines.Coroutine.Pool.PrePool(20); Yggdrasil.Coroutines.Coroutine <Result> .Pool.PrePool(20); }
public void RepeatedTypeDefTest() { var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(TestRunningAction).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var context = parser.BuildFromFiles <TestState>("ParserTests\\repeatedTypeDefTest.ygg"); Assert.IsTrue(context.Errors.Count > 0); }
public void MalformedCSharpTest() { var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(TestRunningAction).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var context = parser.BuildFromFiles <TestState>("ParserTests\\malformedCSharpTest.ygg"); Assert.IsTrue(context.Errors.Count > 0); Assert.IsTrue(context.Errors.Any(e => e.Diagnostics.Count > 0)); }
public void EscapeCharacterTest() { var parser = new YggParser(null, new YggCompiler()); var document = parser.LoadFromFile("ParserTests\\escapeCharacterTest.ygg"); const string innerTextA = @"state.A >= state.B || state.C <= state.D"; Assert.AreEqual(innerTextA, document.SelectSingleNode("/__Main/FilterA/Conditional").InnerText); const string attributeTextB = @"state.A >= state.B && state.C <= state.D"; Assert.AreEqual(attributeTextB, document.SelectSingleNode("/__Main/FilterB").Attributes[0].Value); const string attributeTextC = "state.A == \"hello\""; Assert.AreEqual(attributeTextC, document.SelectSingleNode("/__Main/FilterC").Attributes[0].Value); const string attributeTextD = "state.A == \"hello\" && state.C <= state.N"; Assert.AreEqual(attributeTextD, document.SelectSingleNode("/__Main/FilterD").Attributes[0].Value); const string attributeTextA0 = "state.A == \"hello\" && state.C <= state.N || state.D > 10"; Assert.AreEqual(attributeTextA0, document.SelectSingleNode("/__Main/FilterD/NodeA").Attributes[0].Value); Assert.AreEqual(attributeTextA0, document.SelectSingleNode("/__Main/FilterD/NodeA").Attributes[1].Value); const string attributeTextB0 = "state.A >= state.B || state.C <= state.D"; const string attributeTextB1 = "state.A > state.B || state.C < state.D"; Assert.AreEqual(attributeTextB0, document.SelectSingleNode("/__Main/FilterD/NodeA/NodeB").Attributes[0].Value); Assert.AreEqual(attributeTextB1, document.SelectSingleNode("/__Main/FilterD/NodeA/NodeB").InnerText); Assert.AreEqual(attributeTextB1, document.SelectSingleNode("/__Main/FilterD/NodeA/NodeC").Attributes[0].Value); Assert.AreEqual(attributeTextB0, document.SelectSingleNode("/__Main/FilterD/NodeA/NodeC").InnerText); Assert.AreEqual(attributeTextB1, document.SelectSingleNode("/__Main/FilterD/NodeA/NodeD").Attributes[0].Value); Assert.AreEqual(attributeTextB0, document.SelectSingleNode("/__Main/FilterD/NodeA/NodeD").InnerText); const string innerTextE = @"var manager = new CoroutineManager(); var root = new Sequence(manager); var stages = new Queue<string>(); manager.Root = root; Assert.AreEqual(Result.Unknown, manager.Result); Assert.AreEqual(0UL, manager.TickCount); manager.Update(new State { A = true, B = true, C = true});"; Assert.AreEqual(innerTextE, document.SelectSingleNode("/__Main/FilterD/NodeA/NodeE").InnerText); }
public void ScriptStructureTest() { var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(ParameterizedTestNode).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var context = parser.BuildFromFiles <TestState>("ParserTests\\testScriptA.ygg"); Assert.AreEqual(0, context.Errors.Count); Assert.AreEqual(4, context.TopmostNodeCount); // MyCustomTypeA var nodeA = context.Instantiate("A"); Assert.AreEqual("A", nodeA.Guid); Assert.AreEqual(typeof(Filter), nodeA.GetType()); Assert.AreEqual(1, nodeA.Children.Count); var nodeB = nodeA.Children[0]; Assert.AreEqual("B", nodeB.Guid); Assert.AreEqual(typeof(Sequence), nodeB.GetType()); Assert.AreEqual(3, nodeB.Children.Count); var nodeC = nodeB.Children[0]; Assert.AreEqual("C", nodeC.Guid); Assert.AreEqual(typeof(Condition), nodeC.GetType()); Assert.AreEqual(0, nodeC.Children.Count); var nodeD = nodeB.Children[1]; Assert.AreEqual("D", nodeD.Guid); Assert.AreEqual(typeof(Condition), nodeD.GetType()); Assert.AreEqual(0, nodeD.Children.Count); var nodeE = nodeB.Children[2]; Assert.AreEqual("E", nodeE.Guid); Assert.AreEqual(typeof(Inverter), nodeE.GetType()); Assert.AreEqual(1, nodeE.Children.Count); var nodeF = nodeE.Children[0]; Assert.AreEqual("F", nodeF.Guid); Assert.AreEqual(typeof(Condition), nodeF.GetType()); Assert.AreEqual(0, nodeF.Children.Count); // MyCustomTypeB var nodeG = context.Instantiate("G"); Assert.AreEqual("G", nodeG.Guid); Assert.AreEqual(typeof(Sequence), nodeG.GetType()); Assert.AreEqual(2, nodeG.Children.Count); var nodeH = nodeG.Children[0]; Assert.AreEqual("H", nodeH.Guid); Assert.AreEqual(typeof(Filter), nodeH.GetType()); CheckMyCustomTypeA(nodeH); var nodeI = nodeG.Children[1]; Assert.AreEqual("I", nodeI.Guid); Assert.AreEqual(typeof(Filter), nodeI.GetType()); CheckMyCustomTypeA(nodeI); // Third node. var nodeJ = context.Instantiate("J"); Assert.AreEqual("J", nodeJ.Guid); Assert.AreEqual(typeof(Inverter), nodeJ.GetType()); Assert.AreEqual(1, nodeJ.Children.Count); var nodeK = nodeJ.Children[0]; Assert.AreEqual("K", nodeK.Guid); Assert.AreEqual(typeof(Sequence), nodeK.GetType()); CheckMyCustomTypeB(nodeK); // Parameterized node. var nodeL = context.Instantiate("L"); Assert.AreEqual("L", nodeL.Guid); Assert.AreEqual(typeof(ParameterizedTestNode), nodeL.GetType()); Assert.AreEqual(7, nodeL.Children.Count); var parameterizedNode = (ParameterizedTestNode)nodeL; Assert.AreEqual(1, parameterizedNode.PropertyA); Assert.AreEqual(2, parameterizedNode.FieldA); Assert.AreEqual("hello", parameterizedNode.PropertyB); Assert.AreEqual("goodbye", parameterizedNode.FieldB); Assert.AreEqual(3, parameterizedNode.PropertyC); Assert.AreEqual(4, parameterizedNode.FieldC); Assert.AreEqual(3, parameterizedNode.ArrayPropertyA.Count); Assert.AreEqual("one", parameterizedNode.ArrayPropertyA[0].PropertyA); Assert.AreEqual("two", parameterizedNode.ArrayPropertyA[1].PropertyA); Assert.AreEqual("three", parameterizedNode.ArrayPropertyA[2].PropertyA); }