Muse Behaviour design questions

Hello, I tried to test graph today and I am confused about how it works. At first sight I feel like this tool went towards strange design choices, however I am not going to jump to conclusion yet. I have got several questions:

  1. Why it is possible to connect multiple branches into one? Is that different kind of behaviour tree design? In that case how it behaves when traversing, as it’s not clear for me.
  2. It is a bit strange that some “nodes” are made of multiple other nodes, but I understand the reason behind this, however this also makes some states unclear, does true/false node behave like separate or it’s just visual part of single node?
  3. Is there abort/event system or plan to introduce one? If there is none I assume the whole tree is traversed every single tick to be responsive.
  4. In case there is no abort and whole graph is updated every single time, how do you make it to perform well? I mean if your tree is more than just couple of nodes, then full update is for sure quite costfull and does not scale very well.
  5. Why Sequence is named sequence, but selector does not exist and instead it’s something like try all or queue or something like that?

I’m interested in your questions too but in regards to updating a whole graph if you have well designed entry points for each section that should be ok?

What do you mean by entry points? If you mean to introduce internal state machine like state, then I don’t like it too much personally. However even if you do this, take this example:


(I found it in google)
Let’s assume your tree is inside the first main (left) branch inside last MoveTo node. Next time you tick the tree it would need to traverse all nodes on the left like Run EQS Query, Find Random Point etc. How you actually bypass this? It’s not only expansive, but also completely not desired to run those tasks. Obviously you could introduce another and another enum/bool state, but then it’s not behaviour tree anymore, but state machine. Am I missing something obvious?

1 Like

My trees have entry points based on stats. I.E if food stat <=50 then run node find food etc. All the nodes below food stat do not run if food is >50. This way each tick the only nodes that run are check stats unless a stat is at the required amount. Another question is there a lock selector? I.E in my example my tree then only runs the find food nodes and ignores all the others at that time. Your probably right, my setup is a mix of behaviour tree and state machines.

Hi @Qriva !

I started the project with the idea of simplifying the UI/UX and process of defining and implementing behavior trees and their nodes. Did we succeed in that? I hope so, but I’m sure opinions differ :slight_smile:

  1. When you connect multiple branches into one you should have a pop-up showing how you want the merge logic to be handled with a wait for all (wait for all branches to finish) or wait for any (start the next part as soon as one reaches it). Are you not getting this pop-up?
  2. I’m guessing you are referring to the implicit sequences of nodes that are dragged together? The idea was to reduce the noodle spaghetti mess that is often with other graphs / behavior trees. What do you mean by true/false nodes? And what do you mean by them being separate or visual part of a single node?
  3. There is an event system and there are way to abort! We need better documentation on this. For now let me refer you to a post by my colleague @LauraRantonen here. As for aborts, I want to introduce a better way to do this, but for now a common way to handle it is by running a Run In Parallel Until Any … node and having some looping condition on one side and logic on the other.
  4. The graph isn’t fully traversed at each tick, we cache the running nodes :slight_smile: I would add performance measurements for a single graph can be misleading because of the way we defer certain update calls, but hopefully running many graphs should perform just fine. Hopefully more improvements on this in Q1! :slight_smile:
  5. Good question! We thought about naming Sequence as Run In Order or something. The idea was to rename the technical terms into more clear and friendly terms, like Decorator → Modifier, Leaf → Action, etc. Composite we were torn between Flow / Logic / Sequencing and we may iterate on that. If you have a suggestion for names we’d love to here them!

I hope this answers most of your questions. Feel free to expand on the ones I asked to understand better or come back with more :slight_smile:

P.S. I apologise for the slow response. Technically I’m on vacation and so is most of the team right now :wink:

Sense we cache the running nodes, your selector should keep to what’s selected until it’s finished :slight_smile:

You can use Start On Event nodes to allow things run simultaneously, or one of the potential abort patterns such as “Run In Parallel Until Any completes/succeeds/fails” with a “Repeat Until Success” on one side and the logic in the other.

I’m curious if you have any suggestions for an interrupt pattern, please send them my way! :slight_smile:

My current idea is to have a node called Run Until which branches into 2 parts: “Run”, where you put the logic in, “Until” or “Until Condition”, where you put the condition into. The logic will run and be aborted if the condition returns true. Please let me know what you think! :slight_smile:

I also want to add Utility-Based Decision Nodes to branch according to certain variables and curves. We still need to flesh out the UI/UX for them :slight_smile: I personally want it for my RTS prototype :smiley:

1 Like

No worries about slow response, actually I noticed in other post you are on vacation and I didn’t expect fast response anyway :slight_smile:

Ok, so to refer to my points:

  1. Yes I do get popup, but the behaviour is um… black box. I mean there is no documentation (I know it’s prereleae state) and it’s not familiar to my current knowledge. Even after reading it I am still not sure what I am trying to do. If I create Wait For All, does it mean both parents must be activated? It would mean that tree can traverse multiple branches at once, but this behaviour is uncommon I think? How does it even work, what happens when two branches go into single subbranch? What is the order of execution in this case? How do I even know what “tip” goes first? Any many, many other questions. What I am trying to say is that I simply don’t understand how the tree actually works.
  2. I mean switch and ifs, this:
    obraz
    Is that single node? Or some kind of strange structure made of multiple nodes combined into composite node?
  3. So to make branch responsive I need to put parallels with looped conditions?
    I tried to read the link about event, but I am not sure what it does. It sets some given variables and executes some delegate, but how it’s used, execution jumps to that node or it executes later if event happened?
  4. It is not traversed, then how it works? This is again blackbox, and because of this it’s hard to tell it is good/bad or if this even works properly.
  5. “The nice thing about standards is that you have so many to choose from”. I don’t want to say that trying to improve something is bad, the idea about grouping tasks into sequence box is good UX idea, in my humble opinion renaming some core elements like this is rather bad idea. For example “Sequence” is really good name, it does things in sequence, this is super clear.
    Just to repeat what people says on forums - people do not like when Unity tries to make stuff more “simple” in wrong way, just to make tool more appealing for very green devs, especially visual scripting tools. This kind of things even end in very bad way, for example property declaration in shader graph. They tried to make it more simple and now they need to rework and rename stuff all the time.
    In any case I would not change names for all that stuff, but if you really want to, then I would at least add some aliases, so when i type “selector” in search, then the proper node would appear, suggesting it’s this one.

Maybe I sounds negative, but I am a bit irritated, because Unity had no behaviour tree tool for so long and there was no free alternative, so I made my own, but it’s simple and has very clunky UI (old EditorGUI, what a pain…), so when I noticed this one I had huge hopes, it has nice UI, but when I started playing with it, it felt overengineered and I couldn’t understand how it actually works, and how you plan to solve certain problems.

One additional question, can we create or is there plan to allow custom blackboard variables or component type variables?

Hey @Qriva !

  1. Ok, so the graph runs as you’d expect a behavior tree graph to run, but we have a few extra nodes that make this work: “Run In Parallel” allows you to run multiple branches at the same time and that’s where you’d be most likely to use the merging behavior. Another option is having multiple Start nodes, or Start On Event. You could trigger an event from your main graph and then create a merging point. I’d say the Run In Parallel is the more likely situation to encounter. At the moment we don’t have any sort of validation to tell the user if the node is actually reasonable in that situation, but this is something we might be able to look at (it might not work due to custom nodes users can make)
  2. Right! This is a single node. The True and False are essentially ports. Instead of using sockets like most flow graphs use, we wanted to show “This is branching” and create those branches for you.
  3. The abort situation is a bit lacking at the moment and requires a trick, yes. If you have any suggestion on this please send them over! But right now, using a “Run In Parallel Until …” and having some termination logic in one side and run logic in the other side is the way to make it work. What do you think of my “Run Until” node in the previous post? As for the events, the execution doesn’t jump to that node but it activates it. The variables values assigned to the Send Message node are then set to the variables assigned to the Wait For Message nodes and those are activated. This is another way you could potentially have branches running in parallel. You can also send event messages across graphs and using code. We’ll be improving the documentation on this :slight_smile:
  4. Regarding the Black Box: You can look at the BehaviorGraphAgent.cs to see exactly how it works :slight_smile: What I meant by it’s not fully traversed at each tick is that, let’s say you just started the graph, you do traverse it from the root, but then at some point you hit a node that returns “Running” status, so we cache which node was the last to run and we return for the rest of the frame. Then on the next Tick we don’t start from the top, but from the cached node. This means the evaluation which happens above that node won’t happen again until the branch is terminated or something triggers it, which is why the ways to abort a branch is quite important and we need a more clear way to do it. As I mentioned, we also want to make improvements on how we do this in Q1.
  5. I really like the alias idea! Thank you :slight_smile: I get what you’re saying on the rest of the point here. I don’t necessarily 100% agree but I do agree with some things :slight_smile: It’s true that some people who are already familiar with behavior trees need to adjust slightly, but in the end of the day it’s very slight adjustment including a few names and the ability to sequence items by dragging them onto each other and creating a group instead of having to draw an edge for each of them. Perhaps we could show more explicitly that it is a sequence? Also, I do agree Sequence is a good name, which is why we kept it, but some other names were quite confusing to people not familiar with behavior trees. I’d love to get more user feedback here and see where the consensus is :slight_smile:

I’m sorry you’re irritated about this! I was also extremely frustrated with Unity’s lack of behavior tree!! This is why I started this project. Please don’t think of the decisions gone into it as a monolithic thing done by Unity, but instead feel free to tag me and ask me! I’ve been involved in nearly every UX decision for this project and I’m very likely to be responsible. The good thing? I’m also listening and I want your feedback! Let’s see how we can improve the things that irk users and make it really great :slight_smile:

As for overengineered, I’d love to learn more about that! Please tell me what do you mean specifically by that? Or is it the previous questions? What extra problems do you have in mind?

Bonus Answer: YES!!! I believe this is on our Q1 roadmap. We already have the technical ability to do that and just need to figure out the UI/UX for it :slight_smile: Another thing we want to figure out is how to auto cast them so you can assign a GameObject or Component in Transform, etc, or even Transform into Vector3 (for Move To … for example).

I hope I answered your questions. I’m sorry some of our design decisions have irked you. Please try and keep and open mind, but also if we get overwhelming feedback that people hate it, we can adjust! So far most of the feedback received on this was extremely positive, but now we are getting more and more people to try it and who knows where it’s going to go.

Please remember it’s not actually a black box, the runtime logic is really simple and you can follow it through by opening BehaviorGraphAgent.cs and following it into the Graph and Nodes. The classes that run the graph are relatively simple :slight_smile:

I hope you have an excellent weekend and happy holidays! :slight_smile:

P.S. It’s very cool you made your own graph and put it for the public! I think a lot of your journey into this echos my own, we just diverged a bit on the standard UI representation for behavior graphs :slight_smile: I also recommend this talk by the people at Destiny 2 for potential inspiration: https://www.gdcvault.com/play/1025303/Walking-Talking-and-Projectiles-Storytelling

Ok, after watching the last link I think I understand from where you are coming from.
I think this is also the source of my irritation - what is this muse behaviour actually?

Destiny2 presentation tries to solve some problem, they wanted to code flow of the scene, create some kind of AI timeline and this muse behaviour looks very inspired by that. However I think this is not the kind of graph I expected to get, what I wanted to get is literally standard behaviour tree. To be more precise, the one described by this talk: https://www.youtube.com/watch?v=Qq_xX1JCreI
Obviously this is not how it must be implemented, but I think you get the idea.
Also I am not talking about visual representation (UI/UX), but how the tree itself works.

I am definitely not a behaviour tree expert, plus I only tried your tool a bit, so you should take my words with grain of salt, but I will try to be constructive as much as I can.

Behaviour tree is just construction used to describe the flow of small tasks. The goal (of tool) is to allow creation of decision tree, that controls some behaviour. The tree is not controlled by switching between states like state machine, but the state is described by the “tip” itself (currently running task). One of major behaviour tree problems is being responsive to change of conditions, usually to make responsive tree you traverse all higher priority branches every time to see if something changed (to take different branch), or you make an event driven behaviour tree with abort system (to reset state to proper place).

Now, I am concerned, because as you said in your answer the tree is not traversed every frame, but runs from the tip - what is typical behaviour of event driven tree, but at the same time there is no proper abort like system. I think this is so important that it should be designed as one of the first mechanisms, but from your answer it feels like it’s not properly designed yet.
However, it’s still pre release, it’s not too late. The thing is, because the tree is actually running with multiple “tips” and separate parallel trees there comes the problematic question - what to do when the tree aborts to higher priority node? It would be super hard to track all states and it would be even harder to revert it correctly, it sounds super tricky/impossible to me. This is probably also why unreal has only SimpleParallel node.
I might be wrong about this, possibly I can’t see something obvious, but here comes the main question again - what is muse behaviour actually? Proper behaviour tree or some kind of new structure with different rules?

As for the fifth point about naming - nobody stops you to even rename everything, but it does not mean you should.
Experienced user will need to slightly adjust, so you change zero learning curve to small learning curve for no reason, then for inexperienced user, I think that almost all other resources in the internet use the same naming convention, so it creates unnecessary confusion for those users, in case they use any external resources.

I hope my opinion does not sound too negative :smiley:
In any case don’t take it too personally - ultimately I just want a good tool, so I try to verify some of my thoughts. I have some notes and ideas to UI/UX too, but I will do it in next post after we settle the main question.

Happy Holidays :slight_smile:

Happy Holidays @Qriva !

I hope I’ve not missed anything but the 4 main things I see are:

  1. Comments about the tree running from the leaf nodes and not doing full traversal per frame.
  2. Lack of a fully integrated/explained abort system and confusion with regard to parallel nodes.

I’m happy to expand on this at another day but I’ll keep it short for today.

The first point: Caching the current nodes is actually a very common optimisation for behavior trees and it’s well documented and has different strategies on how to handle, such as re-evaluating every X frames/frequency or abort mechanisms. I don’t believe our approach to it is entirely new :slight_smile:

The second point: Yes, we 100% need a better experience with regard to aborting branches! Perhaps I disagree with regard to the parallel nodes and tracking being complicated as we do keep track of nodes and their parents and can trace it back when we abort a branch and you can do it already with the “parallel until any completes/succeeds” example I mentioned above.

We obviously don’t want to cause too much confusion and there is no need to rename everything for renaming sake. I really like the aliasing idea and I’m pretty sure we’ll add it soon :slight_smile:

Our goal with this is to make a behavior tree, but make it more than just a behavior tree in both UX and hopefully in capabilities. I’d love to hear your feedback after you tried it a bit and I hope that the team’s activity on the discussion pages make it very clear that we are listening, helping, fixing and are open for feedback. Please give it more of a try and let us know how it works in practice while keeping an open mind over the differences from traditional behavior trees :slight_smile:

Hello again,
One note, in this post when I mention “abort system” it will mean the system similar to the one used in event driven behaviour trees.

  1. There is nothing wrong in caching nodes, it is indeed common and I implemented my tree also with suspending execution. My point here is that the tree is not fully implemented in the way it should be, so for example if there is node caching, then there should be abort system (or something similar) implemented from the start to make the tool fully complete in it’s design.

  2. About parallel - if you have real tree without merging branches, then aborting would probably just revert everything below and buble up. However there are various types of abort: Self, Lower Priority and Both. Here is the first problem, should everything below Run in parallel… node be treated like two separate subtrees or just part of this single tree? This is more of design problem than a question.

Consider the tree below, we are running two waits in parallel, then assume the condition (red arrow) is the aborting node.


In case of Self abort, the left wait should be probably aborted or anything running below it.
But what if abort is set to Higher priority or Both? Should we abort parallel (right) wait? If we take into account parallel branch in priority system, then is it really parallel, and does the order of subbranches matter? If not, then can we abort this branch in case they are considered like completely parallel tree?
And very important question, where it actually ends? Do we just reset right side and after abort the left one will continue only? Or we just reset right side to Run in parallel until…?

Consider different graph with merged branches:


There are parallel branches running the same part of the tree (bottom sequence).
When I run the tree, the right parallel branch will finish first and bubble the sucess to the top of the tree, while parallel branch need to finish the last running wait.
Currently the graph stucks forever in the weird state, waiting for the last running wait node, but at the same time it’s in success state. Even if I enable repeat, it never starts again, it’s stuck forever, what I believe is just a bug. However that is not the point here, what I want to show in this example is that in this case branches can’t be even considered separate. What should we do if we enable abort for higher priority nodes? Do we abort everything? each branch does it separately? It’s complicated as there is no just one state, but multiple states.

It would become probably even more crazy if we consider running multiple root nodes with merging points or loops.

To be clear you don’t need to answer all those questions here, I just try to show my point of view and I think everything comes back to:
During the design stage, did you consider problematic cases and you found a solution to them (but they are not implemented right now), or you are trying to solve problems on the fly (que sera, sera)? I mean both common behaviour tree problems, and also things you put on yourself by deciding to add uncommon features or taking different approach.

Hi @Qriva ,

I didn’t watch the video you linked yet as I’m still off, I’ll definitely watch it when I’m back, but I didn’t want to leave you waiting :slight_smile:

  1. I understand your point about aborts for caching. We do have certain abort patterns as mentioned, but we want to add more. This is still early and there is much more to come. If you have a clear example of what you’d expect, please send it over.

  2. Here we control the termination by selecting the right parallel node. In my opinion abort priority like you described it seems confusing. What would a “Lower Priority” refer to, etc. I think we’re being more explicit.

In the first example both branches should be aborted. This is because you’re using “Run In Parallel Until Any”. If you used “Run In Parallel” only the left should be aborted. This is controlled by the Run In Parallel type branches. The “Until Any” means it’ll stop once one of the branches finishes. The Run In Parallel" will continue until all branches finished. We’re still figuring out the UI/UX/Docs to help with this.

In the second example, everything under the Run In Parallel Until Any Completes should also have been aborted. Seeing it’s stuck in a running state suggests there is a bug (as you said) in there that we need to fix and I’ll look at it when I’m back unless another team member gets to it!

As for the design, there are definitely some cases we may have missed and need to consider. We’re hoping to catch most of them via validation, but at the end of the day, in all graph systems I’ve ever used there was always a chance for the user to do something completely bonkers to get undesired results and it does require to use reasonably.

So to answer what I believe your question is: I don’t believe we’ve identified all the design issues. I also don’t think it’s overly flawed in comparison to other graph tools out there. We’re looking to improve it and we’re happy to receive more user feedback :slight_smile:

Hi
Enjoy your vacation, there is no hurry :slight_smile:

Well, sure, it’s rather impossible to consider all edge cases, but I think it is mistake to not consider implications of all mechanisms beforehand, but we will see how it goes, this is just my opinion.

By lower priority I mean nodes/branches to the right. The tree is executed from left to right, so left one is executed first, thus it has higher priority.

About the abort - as I mentioned it does not have to work this way if you have your own and better solution to this problem, but I think expected some kind of event driven behaviour tree with observable blackboard variables, and abort system similar to unreal.
Here is pretty nice explanation how it works there in case you don’t know.

I think your explanation does not fit into common real world needs. Let’s consider the same example for enemy with gun. The left side is used to move agent and right side to aim towards player in parallel.
If there is any abort in movement it’s probably because destination/cover changed or something, and what I probably want is to abort stuff in this part of the branch while the aiming part does not care. If I use what I made, then everything is aborted and this is not what I want. If I use run in parallel, then we are going to wait for aiming to finish and this should stop if we don’t move. Also abort does not mean returning success/failure instantly, but reevaluation and possiblity taking different subbranch.
In any case I don’t say it’s not possible at all to create what I want, but for sure in your graph this common case will be more complicated than it should.
Looks like you see those branches in different way than me, I guess if you watch both my link you should understand my point of view better.

Can you illustrate the aim and move graph for me please? I’m not fully sure I understand your example. Why would aiming stop when you’re not moving? Wouldn’t you always aim, and always move if you have a move target? It almost sounds like these 2 could be their own 2 branches independent from each other.

Sorry, it was made in hurry xD so if this is not good enough I can make it better.

I used move and aim as this is common example for parallel node construction. Obviously you can do as you said if this is what your AI need, but it’s not true that it should happen, maybe there is some other logic when you don’t move like look at some point or whatever and it would collide with “look at player” if that was running all the time.

And yes those two could be completely independent and in my opinion those should be just two separate behaviour trees communicating with each other, this is why I see parallel as kind of separate tree and it should not be considered probably in the other tree aborts, but I can only speak for myself for this.

Hello again, so how it goes?

Hi @Qriva ,

I hope you’re doing well and Happy New Year.

I’m struggling to understand what your example graph screenshot is trying to achieve.

The main issue I’m seeing from your posts is that the optimisation of caching the active nodes lacks a concrete abort, which we’re looking to bring up.

In your examples from the text it looks like you could just use multiple Start nodes which acts essentially as multiple trees / roots for the agent that can work independently.

From evaluating and testing we (the team) are pretty sure the tool can do everything classic behaviour trees can do and even more so personally I’m finding the conversation somewhat confusing and perhaps a matter of personal preference. That said, I can see the confusion that can happen if you’re expecting the tree to re-run from the top every frame and the lack of a clear way to reset the branch. We’ll bring out a solution for that.

If you want help in figuring out how to make a certain tree work, I invite you to detail what you want to see happening and we can look into that specifically.

Hi

The thing is, I do not expect the tree to rerun from the top every frame, all of my provided video clips are talking about event driven trees and it does not happen there.
I wanted you to refer to those videos and abort priority capability and how it would be solved in this tool.

However, you said your team tested capabilities and you plan to introduce some abort system, so if we can’t understand each other vision/needs I can just wait for the implementation and see how it works when it’s done.

Hi @Qriva,

Would you be interested in having a chat with the team over Zoom and see if we can figure out where our understanding might be missing? I just feel wrong about not being able to resolve this properly and I want to make sure we address any issues… :slight_smile:

Thanks!
~ Shanee

Hi
Well, why not - we could talk on zoom/discord if you want.