Easily one the best tutorial I've ever seen. It's good to find people who can explain so clearly while getting to the point. Well done, Mina, and thanks from Italy
This is such an amazing tutorial. My experience with C# is limited to Unity and I am definitely at the beginner level, but I watched this once, then I watched it a second time but coded along with the video, and then watched it a third time afterwards and I feel like I actually understand how it works. I will say I at least understand it enough to have made Behaviors for; Being frightened of a target and running away, getting too far away from the patrol and coming back to a midpoint of the waypoints, going to a target and pushing them away, and breaking up the patrol with random bits of wandering and looking around while standing still. I was using an FSM before and it was turning into a nightmare trying to manage all the if statements inside each other. This is more manageable and easier to make new behaviors with. Thank you! Great tutorial, I learned a lot of useful information!
Hi, and thank you so much! I'm really glad you liked the tutorial and found it interesting, and it's even better if you can tailor your own learning experience from it - yay! Your examples already look you've really gotten into the topic and managed to apply it to a variety of cases: kudos :) Haha, FSMs have their uses, but they can also turn into large undecipherable blobs of spaghetti code if you try to model something too complex... hopefully, now that you know two possible tools, you'll be able to pick one or the other depending on the context! Again, thank you for your nice comment, cheers!
This was really nice. Thank you. I'm subscribing. I believe the 'while' in the GetData is unnecessary, though. When you call GetData on the parent, it will in turn do the same on it's parent, etc. until it finds the data or there are no more parents. I think it works now because the loop exits on the first iteration.
Hi - thanks for your nice and interesting comment, I'm really happy you like the tutorial (and my channel!) :) And you're absolutely right about the 'while', I think I'm doing sort of a double recursion here... woops! Nice catch. We should probably just replace the entire second half by: return parent.GetData(); ... Thanks again, cheers!
Incredible explanation! Subbed! A hint about "out" keyword: you can declare it without needing to cache before hand. An example: object value; if (_dataContext.TryGetValue(key, out value) return value; // It's the same as: if (_dataContext.TryGetValue(key, out object value) return value;
Hello, and thanks a lot for your really nice comment! I'm glad you liked it :) You're totally right about the out, I must have been in an over-detailing mood at this moment ^^ Thanks for pointing this out! Cheers :)
Excellent job. BTs are very hard to describe and build in a non visual fashion but you did it quite well without a lot of fluff. Hope to see your channel grow. You certainly deserve it.
Firstly, I have to say congrats! After years of studying a lot of c# and Unity full courses from the best online academics, I have to say that this is one of the best tutos in most pedagogic aspects I took, also with source Git code. Subscribed
@@minapecheux I'm a mid level unity developer. Not gonna lie, i just HATE AI as i didn't manage to understand basic stuff like this structure since college. You have definally one of the best pedagogic skills i have ever seen on youtube! And thanks fucking god i manage to understand this because of you!
@@abcdefghijmnopqtstuvxz6006 Haha, thanks a lot - I'm really glad the video was useful and you liked it! AI is always tricky, so clearly it's absolutely normal that everyone struggles with it I think ^^ Cheers!
I've watched tons and tons of tutorials in many different tech topics. I don't know if it is the best out there, but this one is definitely my favourite. Everything about it is awesome. Thanks
Great tutorial, and thanks for diving into behavior trees! I noticed something in the ClearData and GetData methods that might help improve efficiency and readability. Currently, both methods use recursion and a while loop to traverse up the tree through parent nodes. This approach leads to redundant traversal, as it iterates up the chain twice and adds extra frames to the call stack with each recursive call. By simplifying each method to use only a while loop, you can achieve the same effect with less stack overhead and clearer logic. This reduces memory usage and improves readability by eliminating the need for recursion. Thanks again for the awesome content - hope this feedback is helpful!
Thank you very much for your kind comment! And yep you're totally right: these methods could be improved and optimized! Sadly I can't modify the video, and it dates back a bit, so I had a bit less experience than I do today... ... but thank you for pointing this out :) Cheers!
Amazing tutorial and channel! Looks at the problem at all levels and explains it all very well. I wasn't sure behavior trees was going to be achievable but this proved otherwise. Thanks!
I like this because it’s seems to be synthesis of a number of different sources, which is really cool and pragmatic. I recognize the code as coming from a Packt book because it has a glaring bug for the sequence node (that “is running” bool variable was a code smell that helped me discover the bug). Ive seen other YT channels rebroadcast that code with the same bug. If you want to see the bug yourself, create a sequence with a behavior in the middle that takes time to complete, and you’ll notice the next behavior starts even though the previous behavior is still running. I also recognize the mono behavior code coming from an RTS blog I stumbled on, which defines an abstract method to generate a BT for subclasses to implement. This isn’t me trying to call anything out. Let this be a warning to everyone to read through code before using it. It is absolutely obvious to me that this creator has good intentions and is an explorer of these complex technical waters. This channel is going to be big because of how it is a genuine exploration (vs other YT channels in this space that are phonies). I hope this comment is received well.
Hi! First of all, thanks a lot for your really interesting and in-depth comment, it's very cool to see someone dive into it ;) So, to react on your various points: - yep, it draws inspiration from various other sources, so it could share some of their bugs; my goal here was to try and sum up the thing in under half an hour ;) - you're absolutely true about this code not handling async/long tasks, I didn't go into all this complexity because I thought it would be too much to cover in this
I did think “I wonder if the video creator and blog creator are the same person” after I posted the comment above. Looks like it is! I don’t have a solution because I’m still grappling with the enormity of questions around BTs. Implicit in the Packt book implementation is that a BT is fully traversed every “Tick” but I think BTs should be allowed partial traversal(?) I flip flop on that point, and the “game ai pro” book I have doesn’t really provide guidance. My solution (1) depends on partial tree traversal and (2) getting rid of the “for each” on the sequence, just assigning the child status to the sequence of it isn’t a success. This setup does have an implicit contract of idempotency, which I now realize I should add to my test suite :) If complete tree traversal is indeed required, I would try to add in some sort of waiting behavior for the “Running” case of the for each. Maybe this is a good use case for an async block? I’ll post links to my code in the next comment, in case YT auto deletes comments with links.
@@andrewallbright (Aha ok - so yep, I dabble in written form too and this tutorial has generated some word of mouth, so I thought you might be talking about this series ^^) Super interesting, again! Yes to be honest, when reading up on BTs, I couldn't really get if it was meant to be a full traversal each time or not... I settled for this because it was simpler and I don't have too big a BT, but this could clearly become an issue with more complex behaviors (which is the whole point of BTs, so... yep ^^). But looking again at the code, I'm wondering all of a sudden...: given that my foreach returns if I hit a failure in processing the child, this should give me early aborts in case a child fails and severely limit the processing of unwanted branches, no? 🤔 I might be missing something - what do you think? (As for the code blocks... yeah it looks like YT gobbles those up - don't hesitate to send those via emails if you get a chance, I'm curious to see what you came up with! Reminder: mina.pecheux@gmail.com) Cheers!
> given that my foreach returns if I hit a failure in processing the child, this should give me early aborts in case a child fails and severely limit the processing of unwanted branches, no? Given we're talking about a full tree traversal I think returning a failure on first fail is desired behavior for a sequence. Any behavior after the failing behavior should not run. If we were talking about partial tree traversal, I would have to think more on that before I provide an answer. E.g. should a partially traversed tree restart again where it left off or should a subsequent traversal start again at the root? I suppose it just "depends" on what the creator wants.
Very helpful, thank you! Going to write something similar in embedded cpp for the art robot featured on my channel. Which I'd known about this kind of structure when I used to make 2D games, would have unlocked so much more functionality
Nice tutorial. I knew I wanted a behavior tree for the AI in my project, and there really is no substitute for just getting a basic toolkit built and implementing a test. I appreciated how streamlined you kept your examples, as well as the cleanliness of your code. I had to customize my own thing for using NavMesh agents, but otherwise, this should be very easy to extend. A *few* things you might have spent a bit more time explaining, like when you used that left shift operator to set the layer mask, but I get you wanted to keep it brief. I'll definitely take a look at some more of your tutorials.
Thanks a lot for the nice comment, I'm glad you liked the video! You are definitely right about some points being a bit glossed over, it was hard to keep it brief and focused... ^^
@Darryl JF Hi! For some reason, your comment doesn't show over here... sorry :/ But thanks a lot, and you're right about the size of the text - I'll try and improve that in my future videos :)
I'm happy you like the video, thanks for your nice comment! Yep Scriptable Objects can be a nice way of composing behaviours - have fun playing around with that :)
@Mina Pêcheux I'm loving this tutorial! I'll be coming back to gradually soak up all the information and, if you're cool with it, I'll drop some questions that may arise when building my own system. Thanks! :)
I'm really happy you like it, thanks so much for your nice feedback! And yep - be sure to ask if you have questions, hopefully I'll be able to help ;) Cheers!
Hi Mina, are behaviour trees recommended for Player/Characters or just for AI? In which case I imagine one would have to handle player/characters with something else, such as hierarchical state machines. My problem is the following: I was working with Visual Scripting and I made a complex system of my own using superstates inside a FSM, and it works really great. Now, I was trying to translate it to c#, so I started learning about State Machines, hierarchical FSMs and behavior trees. Working with such complex tools and systems is easy in Visual Scripting, and by that time, expanding the system felt natural even without knowing the exact tools I was implementing. But now that I look at it, I can't find an exact description of that system. I think it's a mixed of a Hierarchical State Machine with multiple and Concurrent State machines - meaning that in my Base Superstate I have multiple superstates running at the same time and also have other "regular" states inactive and waiting to be transition to, and each supertate has either other superstates (with either more superstates or just sub-states and so on) or sub-states, and also there exists superstates with multiple sub-states that are active at the same time, while other sub-states remain inactive (and are waiting to be transition to to). I hope the explanation makes a little sense at least. Would you say this system would be more similar to a behavior tree than a hierarchical state machine? I'm asking this because as of right now I still didn't dive so much into behavior trees, so I'm not sure (I'm still on the surface). Thank you for reading!
@@guille_sanchez Hello! So, in theory, there is no one-to-one match: you can use FSMs or BTs for AIs or Players. Usually, the way you choose between the two is that: - FSMs are "simpler" than BTs: they rely on more scoped states with a clear beginning and end - if you have "superstates" (and assuming you're talking about some states that regroup others?), you can either keep FSMs but indeed look into Hierarchical FSMs (I've made another tutorial on this topic here :) ruclips.net/video/OtUKsjPWzO8/видео.html), or switch to BTs - hierarchical FSMs are good to keep your code readable and easy, but they can be limited because your states are still quite tightly coupled, so you end up having a lot of shared variables and global data - BTs are usually more "modular": you can start with just a brick of behaviour and then add more step by step as you connect additional checks and tasks The thing that would lean towards BTs in my opinion in your description is the "multiple and concurrent state machines" + "multiple sub-states that are active at the same time" part: as far as I know, in a "normal" implementation of a state machine, you implicitly assume that you have juste one current state, and you use just one state machine on an agent. It's totally doable to have several current states in terms of coding, but it's really not the standard and can quickly give you a big headache since you'll always have to stop and think when "accessing your Player's state" (which one are you referring to?...). BTs are built on this "multiple states" concept since they can handle parallel branches. Basically, depending on the type of flow node you put above your branches, you decide whether the behaviour inside each should run at the same time or in an exclusive way. Also: even though I never worked on that, I have less trouble imagining a game object with multiple BTs than one with multiple state machines. The whole point of FSMs is to centralise behaviour in one place and have your machine manage everything more or less in a bubble. BTs on the other hand can be a bit more "specialised", since they are modular: instead of having just one big BT with hundreds and branches and hard-to-pick flow nodes, you could simply create several BTs that each represent a (coherent and autonomous) part of your full behaviour. I think BTs would be easier to translate to given your current architecture... but I can't be 100% certain since it's just theory ;) I hope it will still be helpful, feel free to comment again (or send me an email at mina.pecheux@gmail.com) if you want to discuss this further :) Cheers!
@@minapecheux Thank you for the very nice and detail explanation! I think you got my point pretty well and I pretty much agree with your assessment. After having some thoughts on it, I think for now I'm going to stick with the hierarchical state machine for the player (and probably add to it some other state machine that can be active at the same time (concurrently)). This is just because I saw other people implementing it (as yourself, thanks for the link!) and I can consult those or get some references if needed. However, I don't discard the possibility of upgrading or reworking the player's script to a behavior tree in the future (for now I'll use it for enemy AI). And by the way, I think it'd be really cool seeing somebody showing a player controller (2D platformer or whatever) with a behavior tree implementation, I don't know of anyone who has ever done that.
@@guille_sanchez I'm happy I could help! Best of luck for your project and AI-learnings :) Hmm, that's right. This could be a great idea for a follow-up on this episode... I will think about it, thanks!!
Thanks for the great tutorial! It's been quite helpful. I do have one question in regards to the way the selector and sequencer works. So, right now I have an object (or respectively the skeleton from your case) and if this object moves out of range from the "Guard", then shouldn't the "Guard" return to it's patrolling behaviour? Right now for me the guard just chases the "Skeleton" endlessly into infinity and I can't seem to find a way to have it return to patrolling when the "Skeleton" escapes the guards range. I'm assuming that when the sequencer starts an action, it'll pursue this until it's completed, which would work fine in most cases but It'd be nice to have some variety where entities will stop performing a task if they're outside a given range. Any Idea on how this can be solved?
You're welcome, I'm glad it helped :) Indeed, if the skeleton gets out of sight, the guard should automatically switch back to its base patrolling state because behaviour trees are re-evaluated continuously (so you shouldn't be "blocked" in a sequence, the tree would actually re-evaluate from the top and either get to the exact same point in this sequence if the situation is the same, or move on to another part of the tree if it's not)... What's your setup? :) Have you simply put the same AI logic on the skeleton as well? Cause for now my two ideas of potential issues could be: - if you've used the same AI: the two characters have the same speed, and so they can never escape each other's field of vision - if you've used another AI: your check for "inside the FOV" isn't working as intended... If you want, you can send me your project via email (at mina.pecheux@gmail.com) and I'd be happy to try and help you debug ;) Hope it helps, Cheers!
@@minapecheux Thanks for the quick response! For the entity that's the equivalent to your skeleton, I have added a simple movement script merely to test this behaviour. Regardless of the speed on each entity, the "Guard" will chase the "Skeleton" endlessly. I'll send over the code if you have the time so you can see it more clearly. Thanks again for all the help! c:
Great guide and explanation. Always wondered how to interrupt a state while doing something. One thing i didnt like is the parent.parent approach since we need to know which node we are in, might be a complex/non-scaleable approach if we have lots of nodes. Instead, couldnt we pass "GuardBT" variable and share the data from there?
Thanks for the kind comment! Yup I totally agree this is not the greatest - my idea was to keep the whole thing as "self-contained" as possible, but it's not very handy here... feel free to adapt this to your liking and improve it for your specific use case ;) Cheers!
Hello Mina! Thanks for this awesome tutorial. Straight to the point. Although I have a doubt, probably a misconception... In the SEQUENCE node, shouldn't a RUNNING state prevent the next state from evaluating? That's not whats happening with this implementation. It's driving me nuts lol. I changed the code in the sequence node to return the state when RUNNING and now it's working as (I) expected. Could you please explain to me whats wrong? Thanks!
Hi - I'm happy you liked the tutorial, thanks for the nice comment :) So there are actually various ways of implementing the Sequence or Selector nodes, exactly because of those subtle differences: depending on the articles I read, some people preferred for the "running" state to be a pass-through (like here) or a blocking step (like in your implementation): both are "ok" because there is no absolute consensus . Therefore it's mainly up to your needs and what you want to code in your project ;) I personally feel like "running" is still undecided, thus it shouldn't block this entire branch upfront, but you can feel differently! I hope it helps :) cheers!
@Gabriel, I am with you on this topic. @Mina's sequence can run multiple actions simultaneously, thus a better name for it would be Parallel. Considering a simple scenario of typical sequence with several actions: "Walk to a door" and "Open a door". The later shouldn't run until first one is completed.
So I really like your tutorials on Unity, and I was wondering, as design patterns are really powerful tools, would you recommend like a good starter kit ? FSM and Behaviour trees, something more ? Thank you for incredible work !
Many thanks for your nice comment, I'm glad you like my content! :) About design patterns, I guess the hard part is that you ideally need to have a basic notion of most of them to properly assess which one is the most suited for your use case; I'd suggest you read this amazing book available totally for free on his website, by Bob Nystrom, called Game Programming Patterns: gameprogrammingpatterns.com/ It's seriously one of my "woah-reads", that really helped me understand the breadth of the topic! Hope it helps, Cheers ;)
Thanks a lot for the clear tutorial, I learned a lot! I have a question. If a child node of a Sequence node is in the RUNNING state, it should wait for this child node to run and return to the SUCCESS state before the next child node can run. Because if the running result of this child node is FAILURE, the next child node should not run.I don't know if I understand it this way, right?
Heya and thanks for the comment - I'm glad the tutorial was helpful! :) Yup, a Sequence node basically proceeds while the children return a SUCCESS! Usually, if you need to have multiple RUNNING children execute at the same time, you use a Parallel node, so there's no need to duplicate the behaviour in a Sequence ;) Hope it clears things out, cheers!
@@minapecheux If you agree with my statement, should the Evaluate method in the Sequence class be written as follows: Case NodeState RUNNING: State=NodeState RUNNING; Return state; Because we should wait for the nodes in the RUNNING state to finish running and return SUCCESS before running the next node. Am I correct in understanding this way?
Heya, sorry I read your comment to quickly before ^^@@jackjia5083 So from what I've seen, there are different ways of implementing behaviour trees and you may stop or not a Sequence when evaluating - I think the important part is that if at least one child node is running, then the Sequence is running too. But again, I believe there are various possibilities here, so depending on your use case, you can totally adapt it and exit early! :) Hope it helps, cheers!
@@minapecheux like a example of the execution flow, in the behavior tree. Then the character move he goes from patrol to detecting enemy and chasing him. Like with arrow in the chart. Sorry if not been clear. In general really good video overall.
@@lovePotato No worries ;) So actually I plan on making another tutorial on how to create a visualizer for a behaviour tree at runtime, so I guess I'll show this kind of execution flow at that moment! But I'll definitely keep that in mind, thanks :)
Thank you for this good tutorial. If you can please change the presentation and IDE to a dark theme, it might be easier on the eyes (creator and viewers alike...)
Thank you for your nice feedback, I'm happy you liked the video :) So to make an inverter you basically need to return a success if the child evaluates as a failure, and vice-versa. If you want to see an "upgraded"/extended version of the BehaviorTree package, you can take a look at my new Github repo for my Unity tutorials over here: github.com/MinaPecheux/unity-tutorials The tuto n°1 actually goes further on behaviour trees and shows how to use this advanced package to create an RTS collector AI ;) I hope it helps, cheers!
Thank you for your nice comment! Yep, sorry - this was an "old" tuto, and since then I've made sure to apply this precious advice :) But thanks for pointing it out! Cheers ;)
Love this tutorial, especially the text version linked in the description. Took me a minute to fully understand the concepts but you’ve done a great job getting me to the point where I can implement this my self. The only big question I have is when we use the SetData() for storing our target. Is there a different way to SetData to the root without using parent.parent? With parent.parent I won’t be able to use this node at any other depth in the tree right?
Hi - glad it was helpful! :) About your question: it is indeed possible to reference the root more directly in each node, so that you don't have to "climb up" the hierarchy like this. (Basically, it's interesting to do this climb if you want your node to work in a standalone subtree that is packaged and reimported, thus self-autonomous - but using the root is way more handy in most "normal cases" ^^) If you're curious to see how to do that, I've actually added this feature in a new version of this lib available on Github: - in the repo of my ebook on AI Programming: github.com/MinaPecheux/Ebook-Unity-AIProgramming - or in the repo of my new (modern) Unity tutorials: github.com/MinaPecheux/unity-tutorials Hope it helps, cheers! :)
Hey, thanks for the tutorial, i have a few questions : - Why did you not pass more data through the constructor, like the speed or animator ? - For a sequence, it is possible to have many checks and multiples tasks ? Or it is better to decoupled them by creating new children ? - Is a behavior tree only deterministic, or it can be stochastic ?
Hi - thanks I'm happy you liked the video :) So about your questions: - we could absolutely pass this info through the constructor but we can either retrieve this data from the transform (with the GetComponent) or from the BT class (with static variables) => "automating"/centralising the info is a way of reducing the risk of human errors ;) - a node can be as complex as you want and it can contain multiple checks or tasks - but it's usually interesting to have pretty "simple" nodes because it makes the entire system more modular - a BT can totally be stochastic: you can have some randomness in your control-flow nodes (ie the Selector or Sequence) so that the children are run in a random order Hope it helps - feel free to post again if you have other questions! Cheers ;)
@@minapecheux Ok thanks for your answer, for my second question, for example, if i want to check if a enemy is in range, classic check, but for example it's a boss, so i want him to have multiple melee attack type, what do i have to do, create 1 script with multiple attack pattern, or separate each different attack ? If separate, do i have to copy another sequence, add some randomness so maybe he'll check among a possible attack with same check ? Or maybe another solution is after the check, create a new sequence with 2 tasks (attack 1 and attack 2) and call the sequence after the check, then the sequence randomly choose a task ?
@@Sky1Max Hey! So, there is no absolute "right" answer, but in general the more you separate your behaviours, the best it is because it makes your system more modular: if you suddenly decide that you want sub-bosses that have some of the special checks but not all of them, you can easily re-inject this little bit of logic in their BT without hassle! :) Adding randomness as some advantages: it makes the behaviour more "human-like" because the players can't always guess the next move... but is also a drawback: the players can't guess the next move! ^^ What I mean that for games like roguelites/roguelikes that rely a lot on mastery, it can be a bad idea because players expect to learn the patterns of your enemies - so randomness can collide with these genres. But in terms of implementation/structure, I'd go with your 2nd solution (even if I don't know the specifics of your architecture here) because it looks to me like it would be the most "efficient" -> you only do the check once, and then have a special "random sequence" that has this randomness auto-integrated and is tuned to handle this situation. I hope it's clear, again don't hesitate to ask follow-up questions! Cheers :)
Thanks for the tutorial, Mina. I'm wondering why _dataContext is in the Node class; why not put _dataContext in the Tree class? Then we don't have to do recursive searches.
You're welcome, I'm happy you liked it :) So the _dataContext could totally be "extracted" to the tree, or the root node; I put it their to have a fully "modular" structure where any node could potentially become the root of a new subtree instantly, and to properly encapsulate the data, but you're right: we could absolutely "specialise"/improve the scripts to better suit the situation and increase the performance! I hope it helps :), Cheers!
Amazing tutorial ! Just one thing, at 17:40, you use parent.parent to access SetData method, will it not be simplier if every node has a reference to the root in addition to its parent ? In big tree it can quickly became messy
Thanks, glad you liked it! :) Yup, you're totally right: depending on the size of your tree, it can be nice to use the root. Here, I relied on the direct hierarchy to be as modular as possible, but this is definitely a pretty specific example case... Hope it clears things out, cheers :)
Hi, thank you for the nice and lightweight starter code. I was wondering something. BT's ask for a return state. In your example the patrol, attack and goTarget actions only allow RUNNING as a return state. I see that the example works fine, but isn't it a bit of a hack, since the possible FAILURE state is provided by the predecessor node, where a SUCCESS state is never achieved. Therefore the provided GuardBT will never return SUCCESS. My point is that, in my view, this "kind of" violates the modularity of BT's since if someone would want to extend the behavior with an additional node at the end with either a sequence of selector it will never be reached. Although BT's heavily rely on the order the block's are placed I think it should not lock out following blocks. What are your thoughts about this?
Hello, thank you for your nice and interesting comment, I'm happy you liked the tutorial! You make a very good point: it is true that in full generality you should try to "isolate" your actions in autonomous subtrees while making it easy to put them together into a larger tree. However, the RUNNING state can be an acceptable state, too :) The flow of your logic depends on your flow-control nodes - what I mean is that it's how you define your Selector and Sequence that you determine if the RUNNING state is blocking or not. And just a little precision - usually, Selectors and Sequences are blocking in some way (either at the first success, or at the first failure for example); it's the Parallels that don't "lock out" the following blocks (if I understand properly your remark) :) But also, to be honest: in this video, I didn't want to spend too much time covering modularity cause it was already quite long, and it's totally possible this got a bit fuzzy in my explanation... In any case: thanks a lot for your question/reflexion, I think it can benefit the other readers as well so it's really cool ;) Cheers!
Hey, you're absolutely true, it would make more sense! ^^ In truth, I initially wanted to let people free of optionally creating their own nodes from scratch, but then edited this out because the tutorial was too long, and then forgot to redo the beginning to adjust for this. But yep, in this setup, you would totally make the Node class abstract to avoid people instantiating it themselves! Thanks for pointing this out :)
Hello, thank you for this helpful tutorial. I'm still a little confused about the "running" state. For example, imagine a sequence node gets evaluated during a frame. After all of its other children return "success", its rightmost child returns "running", which causes "running" to be propagated back up to the root, ending the evaluation that frame. Then, on the next frame, that same sequence node ends up having to evaluate again. Will it evaluate all of its children normally from left to right, or begin by evaluating its rightmost child because it returned "running" in the last frame? Intuitively, I would think it's the latter because you would have to continue running that last child since it's not finished, but maybe I'm missing something about how behavior trees work in general. Also please tell me if anything I described is wrong.
Hi, and thanks for your nice feedback! So with this basic BT implementation, you indeed re rub all possible nodes every time. It's obviously not optimal and not efficient, and real game studios usually have a bit of extra "state logic" to shortcut to the right place and avoid it. But as is, as I only wanted to demonstrate the core principles of the tool, you'd have to re evaluate the nodes, yep ;)
Really well structured tutorial! I should be able to start implementing this into my combat game asap! Just a quick question, could you briefly break down in what situations you'd use a sequencer node vs a selector node? I had some trouble visualizing AND and OR gates in the context of nodes Thanks!
Hello - thanks a lot! I'm really happy you liked the tutorial and found it useful :) About the AND and OR: it's basically about the order of execution, and about whether or not you want the other children to run after one failed. If you use a sequence (=AND), then as soon as one node fails, the entire branch is "dead" and will abort. This is typically interesting when your branch consists in one "check node" and one "task node" (like the "CheckEnemyInFOVRange"/"GoToTarget" duo here). If you use a selector (=OR), then you can have a child that fails, and the tree will continue to try the next children. In other words it's only if all children failed that you failed. This can be useful to get an idle behaviour: you have your "main" branches on the left (=with a higher execution order) that implement your real behaviours like watching out for targets or attacking, and then if all of them fail you end up in your idle branch on the right. I hope it's clear, feel free to tell me if you have other questions/are still confused! Cheers ;)
@@minapecheux Yes, ok! This makes things a bit clearer as to why you organized Sequence nodes under Selector nodes in your example--Selector continues the run the TaskPatrol node even if the Sequence nodes return failure. I think I get it :]
@@minapecheux Another question... Finite State Machines typically have a "SetState" method that determines precisely what state the statemachine is in. What would be the equivalent of that in this implementation of a behavior tree? Would that be the state = NodeState.SUCCESS statements? Because something I liked about FSM is that when the states switch, you can call "OnEnter" and "OnExit" for each state respectively like so: public void SetState(IState state) { if(_currentState == state) return; //do things while leaving the state _currentState?.OnExit(); //swap state to a new state _currentState = state; //do things while entering new state _currentState.OnEnter(); } The advantage to this is that you can, for example, turn off a boolean in the animator over the course of 1 single frame. I'm worried that the Evaluate function cannot perform the same way. Please let me know what you think. Thanks!
@@cyprusplotarmorstudios4396 Hi! So the whole point of behaviour trees is to be a bit more "flexible" than state machines - in particular, you don't actually encode a precise state to enter and exit, but rather you compose it by adding up the reachable nodes in the current context. I don't know if I'm clear ^^ but in short what I mean is that to re-create the OnEnter and OnExit you have to integrate this logic in the structure of your tree itself (for example with some check nodes for the "first frame" and "last frame" of your "state" that sporadically run some specific sub-branches in your tree). I hope it helps, tell me if it's not clear! :)
Amazing tutorial! Very well structured and understandable!. A few questions: -I noticed that once the AI goes into the branches it stays there/loops inside. Is there a way to make it loop back to the top every few seconds? - Also I'm not sure I understood, If I want the check node to go to a specific action node on success how do I call it there? To give perspective, in the project I'm making the AI has 3 options he needs to choose from every 3 seconds, one of the 3 options is dependent on a specific option. The third is always available. And the game loops until one win
Thanks a lot for your nice comment, I'm really glad you liked the video! About your questions: - actually, every frame, the full tree is executed from the root (in the Update() function, you see that you call the logic on the root node): it's just that, most of the time, you're going to end up in one of your branches again and again because all the checks give the same result while the overall situation is similar. Therefore, you feel like the AI just "stays" in the branch - even if, in truth, it's simply re-traversing down to this branch every time :) - the idea is that "transitions" between your nodes (or perhaps more accurately "dependencies") are set by how you define the architecture of the tree: depending on whether you use a Sequence ("AND" node) or a Selector ("OR" node) for example, you can determine the sibling node that should go after a check Oftentimes, it's actually easier to start from the bottom (the leaves of the tree) and then work you way up when you design the architecture. For this precise example, you could think of it this way: 1. I place a "Check" node on the left of my diagram because I want it to be run first (and convention dictates that left = higher priority) 2. if it succeeds, then I should run my "Action" node; else, it should be stopped from executing => I want to have an "AND" parent node above those two nodes (this way, if the first "Check" node fails, the second one won't even be tried) 3. if you want an "else" case: suppose you have an "Action 2" node to run if your "Check" node fails. For now, you have a little branch of 3 nodes already designed: the parent Sequence and its two children, "Check" and "Action". All that's left to do is add yet another parent node above this branch and your "Action 2" node - except this time you want a Selector ("OR" node) => this means that: > the first child of the Selector (= your branch of 3 nodes) will be executed => if it succeeds, the Selector is done and doesn't need to execute the second child (= "Action 2" node) > else if the first child fails (because the "Check" fails) => the Selector tries to run the second child (3b. to have a third option that always plays, you should replace the top Selector node with a Parallel node - more info about this composite nodes here if you want: mina-pecheux.medium.com/making-a-rts-game-23-implementing-behaviour-trees-for-our-units-1-3-unity-c-1a61840058a6) Hope it helps, cheers! :)
Very good tutorial. In Tree.cs, why is Start() protected while Update() is private? Will Update() execute every frame on scripts that inherit Tree (like GuardBT) even if Update is private?
Thanks, I'm glad you liked it! So, in C# (and most OOP languages I believe in fact), your derived classes will automatically "copy back" the logic of their parent's - but they can't access it. So if you need to call the method you inherited, or get/set the variable you inherited, you have to make it protected - this way, it becomes accessible to any instance of the parent class, AND any instance of a derived class :) In the case Update(), Unity takes care of calling it for us, and since we don't override it in any way, we don't need to call the base logic - so we don't need to access it ourselves. Hope it helps, cheers!
Nice tutorial, helped me with the basics of understanding the setup of a behavior tree. But technically, the code is not correct. When a leaf returns "running" the selector or sequencer is supposed to evaluate it again until it returns "Success" or "Failure". In the case of the video, it starts with the first child again in the foreach loop. A simple fix is to keep track of which child is being ticked / evaluated in the selector or sequencer if it returns running and continue from there.
Wonderfully helpful! I had one question though. What's the best way to remove a node from a tree? For example, I have a spawn node that all characters enter initially, but once exited can never re-enter. If I wanted to get rid of the spawn node so that that guaranteed fail evaluation doesn't happen, is there a good way to approach that? Thanks again for a incredibly educational channel!
Hi - and thanks for the nice comment, I'm glad you liked the tutorial :) So about your question: I guess you'd have two options: - either you store a field in your root's data to say "I've been spawned now", and you add a condition on the Spawn node to avoid this branch if the "already spawned" data field is present - or you indeed actually remove the node from the tree; depending on the current structure of the tree, this would be done by simply removing the node from the children list of its parent node (if it's a leaf in the tree), and/or by also reparenting its sub-branch to the node above (if it's in the middle of the tree). I hope it helps, feel free to ask if it's still not clear! :D
Thank you so much, I'm really happy you liked the tutorial! :) So it can change a bit from one behaviour tree (BT) implementation to another but, in the sources I found and learnt BTs from, Sequences were only interrupted in case of failure... Perhaps in your case you should also stop if a child is running, not sure - it can depend on your specific example ;)
@@minapecheux Ah okay that makes sense, thank you! Just started reading about this stuff today so I wasn't sure whether this was a standard or not 😅 Really excellent video, hope your channel blows up at some point! (Figuratively speaking of course!)
@@agirmani Haha, thanks a lot for the support :D Yep it's a complex topic and to be honest, I feel like it can adapted in quite a lot of different ways, so I think keeping an open mind and asking yourself 'why this and not that?' is very positive, here! Feel free to come back and ask other questions, on this video or another, if you want ;) Cheers!
Thanks, I'm glad you liked it! I think so (although to be honest I've never tried it) :) After all, dialogue trees use a similar structure to behaviour trees, and you could use the behaviour tree 'action nodes' to actually output a dialogue I guess... Hope it helps, cheers!
Thanks for the article on medium. Really well written! Can you please recommend some resources which can be helpful to turn this BT into a Visual Editor in Unity?
Thank you for your comment, I'm happy you enjoyed the tutorial :) About your question: do you mean in-game or in editor behaviour trees? - if you're interested in in-editor tree editing, I devoted the very end of my Unity RTS/C# tutorial series to this topic (it was for a technology tree, but the idea is the same :)): mina-pecheux.medium.com/making-a-rts-game-50-implementing-a-technology-tree-1-3-unity-c-1c516ba78712 - for an in-game display: you could use a similar placement technique as the one I discuss in the link above; but you'd probably use the Unity canvas-based UI system, so you'd apply this positioning to Images, and Texts, and all... (- bonus: for an in-game "debug" display: you can also take a look at the Unity old GUI system that makes "poorer" UIs but is quite quick to implement: docs.unity3d.com/Manual/GUIScriptingGuide.html I hope it helps a bit! :)
@@minapecheux Hey, Thanks for the links! I needed an in-editor Tree, to facilitate the debugging on the State for BT AI. I will surely check out the resources you shared and would let you know.
Hello, and thanks for the nice comment! :) So, sorry, I'm not sure exactly what you mean by "message box"? Do you mean printing something on screen? As for the interruption decorator: the idea of this behaviour tree logic is that a specific branch executes only when the condition it contains are ok. Therefore, to "interrupt" a task, you basically need to switch of the flags and variables that make up your context in order to prevent the next cycle from taking this route in the tree structure. There is no real interruption decorator per se, it's more about blocking the flow... Hope it makes sense ;) Cheers!
Why would you use the SetData / GetData methods instead of exposing some variables in the GuardBT class and passing the GuardBT reference through the constructor?
Hi! So the idea is to try and abstract away all the logic/functions that are common to any behaviour tree in the BehaviorTree package, and in particular in the Node parent class. Also, keeping the data at the node level allows you to create more modular (sub)trees to then re-use and re-assemble in various ways ;)
Really good tutorial. Nice job! I am building a different type of game from the on in the tutorial, Its more of a virtual pet game. But your tutorial was very useful. I was wondering, in the last step when you where implementing the attack part, is there a way to make the sprite return to following the waypoints without destroying the object? (because in my game, its TaskBreed not TaskAttack, and so none of the sprite will die)
Thanks for the comment, I'm glad the video was useful :) Basically: - if your TaskBreed node is in a Sequence branch and depends on a check node, then you need to see what conditions are checked for running your TaskBreed node, and make sure those conditions are currently valid to have the node be executed - else if the TaskBreed is a fallback node (so on the right of the child nodes list), you have to make sure that no other branches gets activated and takes precedence I hope it helps you a bit, feel free to tell me if you want more pointers! (either here or by email, at: mina.pecheux@gmail.com) Cheers :)
Hello! There are indeed many ways of handling the data :) Here my goal was to make each node as self contained as possible, so that you could "cut your tree into subtrees" wherever you wanted... ... but it might not be the most efficient in a lot of cases, and you're right that oftentimes we rather have a global data storage for the entire tree called blackboard:) (Actually, I made this change in a more recent version of Unity behavior trees, which you can find on my Github: github.com/MinaPecheux/unity-quick-tutorials) Hope it helps! Cheers :)
Hi! Thanks for making these awesome tutorials! I have a beginner question. Starting at 9:33, why do you need to set "parent = null;" in the first constructor, but not in the second constructor?
Thanks, glad you like the videos! :) Hmm... it's actually a little forgot bit, I believe, sorry ^^ In fact, the parent should be initialised to null, so it should do the same in the end... but you're right, we could explicitly set "parent = null" in the other constructor too!
17:40 At line 26 -29 you directly set it to parent. parent cause that's the root. But what if you didn't know the root's exact number upwards? Wouldn't it be better just in case to make a loop that goes up the tree until there's no more parents, then sets the data at the last parent?
Hey! So one of the good things with behaviour trees is that they can be modular and fairly self-contained... typically it can be a good idea to encapsulate the data that is related to a specific part of the behaviour to this part of the tree :) But it's just a quick idea here - you could also decide to compute a root once and for all, and tell all the nodes beneath it to consider this one as the root. I hope it helps, cheers! :)
Thank you so much, I'm glad you found the tutorial useful! :) I think it should work - you could either simply have the entire tree tick happen in another update hook, or give your nodes multiple update methods, and call the proper one in each *Update() method from your BTree class (and propagate this *Update() method call to the children)... Haven't tested it, but I don't see why not ^^ Hope it helps, cheers! :)
Hi Mina, first of all very nice tutorial about behavior tree, and may I ask a few questions. I'm new to behavior tree and I read some articles about it and there is a node called 'Decorator', I didn't see any decorator defined in your video so I wonder if u can make a video about how to implement these too. Also I wonder how's the performance of this because tree is calling root.evaluate every update, so if unit number is large and the tree has a lot nodes, will it cause performance issue? Finally, subscribed your channel!
Hello - thanks for your comment, I'm happy you liked it! :) So the 'Decorator' node in behaviour trees is not an actual node in itself, it is a category of flow-control nodes. As I say in the video, all the inner nodes of the tree control the flow, but there are 2 types: the composites and the decorators. Composites have multiple children (eg the Selector or the Sequence) while decorators have only one (eg a "NOT" operator to reverse the success/failure of the child or a Timer that executes a given action every X seconds). In other words: the Decorator class in itself doesn't really exist, rather we usually implement a specific Decorator (for example the "NOT" or the Timer). If you want to see trees that are a bit more complex and that use some decorators, you can check out the RTS tutorial I did and in particular episodes 23 to 25 :) mina-pecheux.medium.com/making-a-rts-game-23-implementing-behaviour-trees-for-our-units-1-3-unity-c-1a61840058a6 I hope it helps - cheers ;)
About performance : Evaluate only proceeds on viable branches and abandons branches that are not viable so (correct me if I'm wrong): Runtime complexity is approximately O(N Log N) which is not too bad.
I'm experimenting with this system. One question I have, is the sequence node supposed to play all the running child nodes simultaneously? From what I understand it's supposed to play them in order one after another.
Hello! So from what I've read, it depends on your implementation; both exist. In my system, I've voluntarily chosen to block the execution only if the child node fails, but you can totally block the execution if they're running, too. It depends on what you want :) Note however that, most of the time, if you want multiple behaviours to run at the same time, you don't use a Sequence but rather a Parallel composite node ;) Hope it helps, cheers!
I'm confused why the dictionary is not shared between all tree nodes instead of each node having its own. It seems really inefficient to access different dictionaries to find one variable or I'm missing something.
Hello! So one big advantage of behaviour trees is that they are very modular. This means that, in theory, your final tree can be composed of several subtrees that each handle a specific task... and are each autonomous! Therefore to be totally generic, I showed a version where any node can be the root of a specific independent subtree, and you can keep the data local to this part of the tree. But of course depending on your use case, it can be valuable to simplify/modify this and have just one big pool of data to refer to :) I hope it helps, cheers!
Thank you for the tutorial! I had a question though, what if one of the behaviours requires a coroutine to complete it's functionality? How would I handle the return statement here for the Evaluate method? And since the behaviour inherits from the Node class, I can't use coroutines to inside them. Is there a solution to this?
Hi! Thanks for the tutorial, best one I've ever seen of behaviour tree in unity. But I had a problem while following it, I had a problem with the patrol part of the Behaviour Tree. I followed the previous Patrol AI video of yours and it works but the patrol system in the behaviour tree doesn't, which doesn't make sense to me since they are the same. I thought this could be a problem with my animations, rigs, and so on, but everything was right. The AI is simply ignoring the waypoints. Any idea of what the error may be? Thanks in advance
Hey! Thanks for your feedback, I'm glad the tutorial was useful :) Hmm, just in case: did you assign the waypoints properly in the inspector? Or else, perhaps you can put some debugs at various points to understand where the logic "breaks"? Also, if you want me to take a look, you can send me your code via email at: mina.pecheux@gmail.com. Hope it helps, cheers :)
Très clair, merci. Le seul truc qui me gêne sont les appels à GetComponent dans les 2 nodes Check et dans le node TaskPatrol. J'aurais plutôt fait cela dans GuardBT, et ajouté l'animator dans les paramètres des constructeurs. Bien sûr, ce ne sont pas 2 appels supplémentaires qui vont ruiner les performances, c'est juste de rester sec. Pardon. DRY. Unity étant revenu sur ses FEE, revenez-vous sur votre décision de ne plus faire de tutos ?
Merci pour le gentil commentaire, et content que la vidéo vous plaise :) Ouip, on pourrait clairement améliorer la separation of concerns - c'est vrai que j'ai essayé de trouver la bonne balance entre "faire un truc clair et rapidement explicable" et "quand même coder quelque chose d'efficace et bien rangé"... pas toujours simple de choisir sur certains points ^^ Pour l'instant, même si je trouve que la lettre ouverte de Unity à la communauté est un grand pas en avant, je ne prévois pas de revenir aux tutos Unity - en gros, parce que ça a été pour moi un bon motivateur pour (enfin ?) passer à une engine open-source, plus en accord avec mes principes de partage et de gratuité :)
Nice tutorial. A few caveats. In Sequence:Node in case RUNNING should be return with RUNNING, not continue to next Node. Visual part (animation) should be separated from behavior. Dictionary of wrapped objects create garbage. Some other small things. I have different problem. I have to run node asynchronous from Evaluation(). But only one should be running at any given time. Not sure how to do it yet. I have some naive ideas (like adding Stop() method and storing last .Running node) but it's getting complicated and kind of dirty.
Heya! Thanks for your comment and for your feedback :) About your question on async evaluation methods - I haven't tested it myself but I believe there are two possible ways of handling that: - if you need a real async function, you can use either a C# async Task or a Unity Coroutine, and run it from the parent MonoBehaviour script; and in any case, you pass it a callback to run when the function is done. Typically, this callback could change a flag in your action node so that it can return a Running or a Success/Failure state... - if you just need a behaviour to run over some period of time, you can always use the Update() and gradually increase a timer to approximate this. But I'll admit those are just ideas, and again I haven't actually tried them out. Hope it helps anyway, cheers!
Hello there! I have a question about your code. In script "Node.cs", you declare access modifier of "constructor of Node class" by "public". But i think "Node" couldn't be exist itself. So i want to suggest to declare this access modifier by "protected". How about it? And... sorry for my bad English :(
Hello! You're totally right: we could/should turn the constructor into a "protected" one since we're not supposed to instantiate the Node class directly but rather use its child classes :) I didn't dive into these "details" because I thought the tutorial would be a bit long if I also dived into inheritance and data encapsulation gotchas ^^ Nice remark, thanks for pointing this out! Cheers :)
@@minapecheux Oh that was really quick reply! Thanks a lot of your tutorial video and reply. It really helped me to implement my Behaviour Tree. Even though i'm not good at English yet, your video was very simple and made me sence. From South Korea...
@@kimbab-nk9jc I'm really happy you liked the tutorial and found it useful for your project! And frankly, no worries: your English seems quite okay to me! Best of luck for your other programming ideas, cheers ;)
@@sk.mahdeemahbubsamy2857 That's definitely a good idea! I already have various platforms/socials to handle at the moment so it might be a bit difficult at the moment, but I will try and see if I can organize this someday! Thanks for the idea :)
Hi, do you have any advanced examples? Let's say enemy has 10 actions, how would I manage the complexity at that scale? What order do I write the actions in? I'm trying to find examples on bigger trees but it's really hard, any help would be appreciated! Thanks
Hello! So: 1. the nice thing with behaviour trees is that you can actually build them gradually, by adding the nodes one at a time and expanding on the possible actions of your unit - so you could theoretically cut down the work and not have to do the 10 actions together :) 2. the order of the actions depends on the priority you want to give them ;) Remember that given how we've implemented the Sequence/Selector control flow nodes, the actions on the left (i.e. the first children) are run first, then the second children are run, etc. 3. it's a bit hard to talk of "an abstract set of 10 actions", and I can't tell for sure where the complexity would reside in implementing your BT. But if you want a more involved example of a behaviour tree, I've actually discussed how to apply this exact design pattern in my series of RTS tutorials, more specifically in this sub-series of episodes: mina-pecheux.medium.com/making-a-rts-game-23-implementing-behaviour-trees-for-our-units-1-3-unity-c-1a61840058a6 You'll see that I add more and more behaviour to my units, bit by bit. I hope it helps, feel free to comment again if you want more info! Cheers :)
@@minapecheux One of the recent problems I had with behaviour trees is when an animation was only running in one node, I had an "attack" node which required "aiming" animation, but for the rest 9 actions i didnt want aiming. I ended up having this: ATTACK AIM = TRUE INVESTIGATION AIM = FALSE WALK AIM = FALSE IDLE AIM = FALSE a lot of repeated code, any tips on not having that?
@@killereks I guess I would have some default value for AIM, set to FALSE, and so I only need to define it once in the "attack" node by setting it to TRUE in that specific case... but again, it kinda depends on your setup, animator, etc ;)
Thanks, glad you liked the video! :) Hmm, to be honest, I've never seen a non-recursive tree... from what I understand of this data structure, it's pretty inherent to a tree to be recursive, since it's all about stacking up nodes to form sub-branches ;) But I could totally have missed something, so I hope you'll find a trick that suits your needs! Cheers.
@minapecheux Well, guess its time to tread unknown grounds! My goal was wrapping several behavior trees underneath a Utility System/AI which decides what Behavior Tree I should flow through, dynamically changing behavior trees based on an AI's current situation.
Hey! So it depends on the behaviour you want for your tree, and more precisely the way the flow nodes should work. Basically: - a "running" state indicates that a node (and the children that are considered given the current situation) are currently computing something and haven't finished - for example, if your character moves from point A to point B, you can consider that while it's moving, the "move" node is in running mode - a "success" state indicates that a node (and the children that are considered given the current situation) has finished its computing and managed to do everything has intended - for example, when your character reaches point B. (the "failure" state is just the reverse: you stopped at one point during the computation because something went wrong... for example the terrain changed for some reason and your character cannot reach point B anymore!) But again it's all quite specific to the specifics of your behaviour tree ;) I hope it helps! Cheers.
Hey Mina! Thanks so much for this tutorial, it's really helping me a lot. One question, if I add another soldier in the scene and they both go after the enemy with one soldier killing it before the other, I get a "MissingReferenceException: The object of type "Transform" has been destroyed on the other soldier. It's occurring during the CheckEnemyInAttackRange script. How can I check if the enemy is destroyed and go back to the root?
Hello - thanks for your nice comment, I'm really happy you like it! :) Nice catch: indeed you would have to check that the Transform still exists. To do that, you can simply use a: if (!t) { ... } that will run if the referenced Transform has become invalid. I guess you'd also have to add this if-check in the CheckEnemyInFOVRange, just to be sure ;) I hope it helps - feel free to tell me / send me some code via email (mina.pecheux@gmail.com) if it doesn't work or you have further questions! Cheers :)
The patrol and target scripts work great but i do have a question. for the attack the ai does not stop to attack and just clips into the object with the enemy layer. is there anything that i could be missing to cause this?
Hello! Happy to know you're interested in the tutorial and the first features are working :) Hmm, did you make sure to set the "attackRange" variable on the GuardBT script? Perhaps it's still with its default 0 value, and saw the CheckEnemyInAttackRange doesn't fire? Feel free to post another comment if that doesn't work! Cheers :)
@@minapecheux thanks for the response. after checking i upped it to both 5 and 10 for testing however the ai still just clips into the enemy without attacking. i should also add i do have the animator stuff commented out atm but unless that is the issue the script should be the same in the vid
@@caolanwalker4042 If you want, you can send me your Unity project (at mina.pecheux@gmail.com) and I'd be glad to try and help you out :) (because it's not easy to do "remotely" like this, honestly I don't have any quick gotchas that pop to mind right now ^^)
@@minapecheux i have sent on a email with both the project file and a link to the github if u wish to clone it. i would like to say thank you so much for taking your time to help out with this
I have a question and am sort of new to this concept of behaviour trees but what is the purpose here of the nodes knowing what parents they have, in all other examples they only know the children and its there job to just update the child. All data shared seems to be accessed through a passed in behaviour tree which they all have access to. So is there a benefit foe this traversing up and down the tree mechanic you seem to use?
Hi! It is absolutely a personal preference - we could also use a shared data storage that's completely decoupled from the tree. I find that storing the data directly in the tree allows me to better organize it and associate some branch-specific data to its corresponding nodes... but you can of course go for another (one-way) architecture, instead! :)
@@minapecheux Sorry if im not being clear so in your example you seem to do what i have seen in previous examples by having a blackboard variable in the tree class which they all query against but im unclear why you need to traverse up and down the tree. What is that used for?
@@charg1nmalaz0r51 Ah sorry - so if I understand properly your question... :) So - the reason I go "up and down" is because I don't have one global blob of data. I have one block of data per node (notice that my _dataContext variable is stored on a Node instance, not the entire Tree). It's empty by default but I can fill it with whatever I want since I'm using a Dictionary with object-typed values. This data is then accessible to my node and all its children, since they can "crawl back" the chain of ancestors. So, for example, I can define a variable in my root node, and it will be accessible to all nodes in the tree. But the nice trick is that I can also define a variable also in a little "subtree" of my big behaviour tree, and only this part will know about it! This helps a lot with encapsulating your data, and it allows you to "import/export" your subtrees to re-integrate behaviour from elsewhere. Re-importing would be harder with one big blob of data because you'd have to constantly update it when you bring in back a behaviour subtree from somewhere else - so you'd lose part of the "autonomy" of your subtree. Does it help a bit? :) Feel free to tell me if it's still not clear!
@@minapecheux gotcha so your seperate nodes hold their own data that it fills out the blackboard with if it needs to be shared otherwise its private to that node so if you want to make a new tree it doesnt have to be tightly coupled. For my purposes i probably wont reuse it so im going to make it slightly esier on myself with the tree knowing everything it needs to at all times.
@@minapecheux I have one more question if i may, how does a behaviour tree handle scenarios where your performing an action and its midway through the action and a higher node is selected because its conditions are met. Say for example the current node thats playing needs to finish or reset before switching over to the other node. Off top of my head maybe like an animation is told to play and it doesnt make sense to just jerk out of it instantly
I really tried to follow this the best I can but could not get it to work. The Guard gets stuck on going to 1 waypoint forever. It also keeps rotating around the waypoint.
Hi, and sorry to hear the tutorial is causing you problems! Just to be sure: are you using the basic translation-based method shown here, or are you using a navigation mesh with a NavMeshAgent? (Cause having the unit "turn around the point" is typically a problem you can see if you don't have the right values for a nav agent ^^) If you're using the technique shown here, then perhaps you need to adjust the distance check threshold (0.01 in the video) to better match the speed of your guard unit? Hope it helps, cheers! :)
@@drewpoling6838 Hello! Indeed, if you're using 2D physics (for example 2D colliders), then you'll have to use the 2D methods as well: Physics2D.OverlapCircleAll, Physics2D.Raycast... Hope it works for you, cheers! :)
@@minapecheux also for anyone else trying this in 2D - take the line _transform.LookAt(target.position); off in TaskGoToTarget. With this on your sprite will rotate on z and you wont be able to see it
Hey! So here I'm not using a tag but a physics layer, actually, in order to optimise the search for enemies in the execution of the CheckEnemyInFOVRange node - if you take a look at 17:20, you'll see that we declare an "_enemyLayerMask" variable, and we set it to be the layer n°6 :) So you'll need to go to the Inspector and set a new layer, using the Slot 6 (or another slot if it's already in use, but in that case remember to update the variable in the code!), and of course assign this layer to your enemy. Depending on your enemy prefab, you might have several nested objects - be sure to set (at least) the enemy layer for the object with the collider. Hope it helps! Cheers :)
@@cioboatamihai-adrian1369 Hum, no ^^ in theory, the layers work the same However!! If you're in 2D, you'll probably need to use the 2D-physic functions, and in particular here Physics2D.OverlapCircle() (docs.unity3d.com/ScriptReference/Physics2D.OverlapCircle.html)
@@minapecheux hey i managed to make it work instead of vector3.distance i used vector2.distance and it worked :D thank you so much for this amazing tutorial and for your time. really learned a lot today :D
It makes it easier to use any node as the root of its own subtree, and thus have a completely modular system :) But you're right in that we could totally "mark" a node as root and only then, give it a data context! Hope it clears things out, cheers :)
Hello! So StartCoroutine can only be called from a MonoBehaviour, and since our TaskPatrol is a basic C# script, you can't use it here. But if you need some async behaviour to happen here, you can also rely on the C# tasks and the async/await C# syntax - if you're curious, I've talked about it and how it compares to coroutines in an article over here: medium.com/c-sharp-progarmming/running-async-code-in-unity-in-edit-mode-d9863cb2726f (in the second half of the post) Hope it helps, cheers :)
Could somebody help me? I did exactly the same as shown in the video except that my waypoints are only 3 and instead of a skeleton I got another unit that has another unedited Version of the "EnemyManager" script attatched to it. It marks everything from every script where "Evaluate();" get's called as an error. I'm kinda confused :D --> "Object refference not set to an instance of an object" I know what this usually means but in this case....
Hello! I'm sorry you're having some issues with the tutorial! About your error: do you have a bit more info about this null reference error? (In particular, if you click on it in the Unity console, you should be able to see which exact file+line it comes from > this will help you understand what causes this error ;) ) Out of the blue, the only things that come to mind in terms of references are: the waypoints themselves (that you have to assign in the GuardBT script), or perhaps a little mistake somewhere in the "SetupTree()" method that causes the children and parents to be improperly connected... If you want, feel free to send me more details/your project by email, at mina.pecheux@gmail.com. I'd be glad to take a closer look and try to solve this problem with you! :) Cheers!
Easily one the best tutorial I've ever seen. It's good to find people who can explain so clearly while getting to the point. Well done, Mina, and thanks from Italy
Thanks a lot for your very nice comment! I'm glad you liked the video and found it useful :)
Cheers!
This is such an amazing tutorial. My experience with C# is limited to Unity and I am definitely at the beginner level, but I watched this once, then I watched it a second time but coded along with the video, and then watched it a third time afterwards and I feel like I actually understand how it works.
I will say I at least understand it enough to have made Behaviors for; Being frightened of a target and running away, getting too far away from the patrol and coming back to a midpoint of the waypoints, going to a target and pushing them away, and breaking up the patrol with random bits of wandering and looking around while standing still.
I was using an FSM before and it was turning into a nightmare trying to manage all the if statements inside each other. This is more manageable and easier to make new behaviors with.
Thank you! Great tutorial, I learned a lot of useful information!
Hi, and thank you so much! I'm really glad you liked the tutorial and found it interesting, and it's even better if you can tailor your own learning experience from it - yay!
Your examples already look you've really gotten into the topic and managed to apply it to a variety of cases: kudos :)
Haha, FSMs have their uses, but they can also turn into large undecipherable blobs of spaghetti code if you try to model something too complex... hopefully, now that you know two possible tools, you'll be able to pick one or the other depending on the context!
Again, thank you for your nice comment,
cheers!
This was really nice. Thank you. I'm subscribing.
I believe the 'while' in the GetData is unnecessary, though. When you call GetData on the parent, it will in turn do the same on it's parent, etc. until it finds the data or there are no more parents. I think it works now because the loop exits on the first iteration.
Hi - thanks for your nice and interesting comment, I'm really happy you like the tutorial (and my channel!) :)
And you're absolutely right about the 'while', I think I'm doing sort of a double recursion here... woops! Nice catch.
We should probably just replace the entire second half by: return parent.GetData();
...
Thanks again,
cheers!
Incredible explanation! Subbed!
A hint about "out" keyword: you can declare it without needing to cache before hand.
An example:
object value;
if (_dataContext.TryGetValue(key, out value)
return value;
// It's the same as:
if (_dataContext.TryGetValue(key, out object value)
return value;
Hello, and thanks a lot for your really nice comment! I'm glad you liked it :)
You're totally right about the out, I must have been in an over-detailing mood at this moment ^^
Thanks for pointing this out!
Cheers :)
@@minapecheux haha I'm loving your videos :D Have a nice day!
Excellent job. BTs are very hard to describe and build in a non visual fashion but you did it quite well without a lot of fluff. Hope to see your channel grow. You certainly deserve it.
Hi - and many thanks for your very nice comment :)
I'm really happy you liked it and found it interesting - cheers!
Firstly, I have to say congrats! After years of studying a lot of c# and Unity full courses from the best online academics, I have to say that this is one of the best tutos in most pedagogic aspects I took, also with source Git code. Subscribed
Thank you so much! I'm really happy you liked the tutorial and found it instructive, cheers :)
@@minapecheux I'm a mid level unity developer. Not gonna lie, i just HATE AI as i didn't manage to understand basic stuff like this structure since college. You have definally one of the best pedagogic skills i have ever seen on youtube!
And thanks fucking god i manage to understand this because of you!
@@abcdefghijmnopqtstuvxz6006 Haha, thanks a lot - I'm really glad the video was useful and you liked it! AI is always tricky, so clearly it's absolutely normal that everyone struggles with it I think ^^
Cheers!
I've watched tons and tons of tutorials in many different tech topics. I don't know if it is the best out there, but this one is definitely my favourite. Everything about it is awesome. Thanks
Thank you so much! I'm really glad you liked the tutorial and found it useful, thanks for the really kind word :)
Great tutorial, and thanks for diving into behavior trees! I noticed something in the ClearData and GetData methods that might help improve efficiency and readability.
Currently, both methods use recursion and a while loop to traverse up the tree through parent nodes. This approach leads to redundant traversal, as it iterates up the chain twice and adds extra frames to the call stack with each recursive call. By simplifying each method to use only a while loop, you can achieve the same effect with less stack overhead and clearer logic. This reduces memory usage and improves readability by eliminating the need for recursion.
Thanks again for the awesome content - hope this feedback is helpful!
Thank you very much for your kind comment! And yep you're totally right: these methods could be improved and optimized!
Sadly I can't modify the video, and it dates back a bit, so I had a bit less experience than I do today...
... but thank you for pointing this out :)
Cheers!
Amazing tutorial and channel! Looks at the problem at all levels and explains it all very well. I wasn't sure behavior trees was going to be achievable but this proved otherwise. Thanks!
Thanks for your kind comment! I'm glad the video was useful to you :)
I like this because it’s seems to be synthesis of a number of different sources, which is really cool and pragmatic.
I recognize the code as coming from a Packt book because it has a glaring bug for the sequence node (that “is running” bool variable was a code smell that helped me discover the bug). Ive seen other YT channels rebroadcast that code with the same bug. If you want to see the bug yourself, create a sequence with a behavior in the middle that takes time to complete, and you’ll notice the next behavior starts even though the previous behavior is still running.
I also recognize the mono behavior code coming from an RTS blog I stumbled on, which defines an abstract method to generate a BT for subclasses to implement.
This isn’t me trying to call anything out. Let this be a warning to everyone to read through code before using it.
It is absolutely obvious to me that this creator has good intentions and is an explorer of these complex technical waters. This channel is going to be big because of how it is a genuine exploration (vs other YT channels in this space that are phonies). I hope this comment is received well.
Hi! First of all, thanks a lot for your really interesting and in-depth comment, it's very cool to see someone dive into it ;)
So, to react on your various points:
- yep, it draws inspiration from various other sources, so it could share some of their bugs; my goal here was to try and sum up the thing in under half an hour ;)
- you're absolutely true about this code not handling async/long tasks, I didn't go into all this complexity because I thought it would be too much to cover in this
I did think “I wonder if the video creator and blog creator are the same person” after I posted the comment above. Looks like it is!
I don’t have a solution because I’m still grappling with the enormity of questions around BTs. Implicit in the Packt book implementation is that a BT is fully traversed every “Tick” but I think BTs should be allowed partial traversal(?) I flip flop on that point, and the “game ai pro” book I have doesn’t really provide guidance.
My solution (1) depends on partial tree traversal and (2) getting rid of the “for each” on the sequence, just assigning the child status to the sequence of it isn’t a success. This setup does have an implicit contract of idempotency, which I now realize I should add to my test suite :)
If complete tree traversal is indeed required, I would try to add in some sort of waiting behavior for the “Running” case of the for each. Maybe this is a good use case for an async block?
I’ll post links to my code in the next comment, in case YT auto deletes comments with links.
Looks like my comment with URLs did get auto deleted. I’ll send you an email with links, for your consideration.
@@andrewallbright (Aha ok - so yep, I dabble in written form too and this tutorial has generated some word of mouth, so I thought you might be talking about this series ^^)
Super interesting, again! Yes to be honest, when reading up on BTs, I couldn't really get if it was meant to be a full traversal each time or not...
I settled for this because it was simpler and I don't have too big a BT, but this could clearly become an issue with more complex behaviors (which is the whole point of BTs, so... yep ^^).
But looking again at the code, I'm wondering all of a sudden...: given that my foreach returns if I hit a failure in processing the child, this should give me early aborts in case a child fails and severely limit the processing of unwanted branches, no? 🤔
I might be missing something - what do you think?
(As for the code blocks... yeah it looks like YT gobbles those up - don't hesitate to send those via emails if you get a chance, I'm curious to see what you came up with! Reminder: mina.pecheux@gmail.com)
Cheers!
> given that my foreach returns if I hit a failure in processing the child, this should give me early aborts in case a child fails and severely limit the processing of unwanted branches, no?
Given we're talking about a full tree traversal I think returning a failure on first fail is desired behavior for a sequence.
Any behavior after the failing behavior should not run. If we were talking about partial tree traversal, I would have to think more on that before I provide an answer. E.g. should a partially traversed tree restart again where it left off or should a subsequent traversal start again at the root? I suppose it just "depends" on what the creator wants.
Sincère bravo, c'est d'une aide et d'une clarté précieuses !
Merci beaucoup ! Ravie que ça vous plaise/aide :)
Cheers!
Thorough, fast, well explained. I don't know how your subscriber count is below 100k. Excited to see what other videos you have.
Thank you for your super kind and supportive comment! I'm glad you like my content :)
Very helpful, thank you! Going to write something similar in embedded cpp for the art robot featured on my channel. Which I'd known about this kind of structure when I used to make 2D games, would have unlocked so much more functionality
I'm glad it could be of help, thanks for the nice comment! :)
This tutorial is well structured and easy to follow, keep it up.
Hi, thanks for your nice comment! :)
Wonderful tutorial, please keep it up ! You seem to put in much thought on how to present a topic in a understandable manner. Subscribed!
Hi - thanks so much for your nice comment! I'm really happy you liked the video and that I managed to convey some of my passion :)
Cheers!
Nice tutorial. I knew I wanted a behavior tree for the AI in my project, and there really is no substitute for just getting a basic toolkit built and implementing a test. I appreciated how streamlined you kept your examples, as well as the cleanliness of your code. I had to customize my own thing for using NavMesh agents, but otherwise, this should be very easy to extend. A *few* things you might have spent a bit more time explaining, like when you used that left shift operator to set the layer mask, but I get you wanted to keep it brief. I'll definitely take a look at some more of your tutorials.
Thanks a lot for the nice comment, I'm glad you liked the video! You are definitely right about some points being a bit glossed over, it was hard to keep it brief and focused... ^^
Just found this channel, super cool stuff! hope you keep going!
Thanks for the sweet comment! Don't plan on stopping, don't worry ;)
You make it look so simple! 😁
I think I need to scrap my old tree system, and just create a new one based on your tutorial instead.
Haha, thanks! I'm really glad you liked the video and can use it for your own projects :)
Cheers!
Thank you for making useful tutorials :)
Thank you Mina. This is very very clear. I love your patience. Have a nice day !! XD.
Thanks a lot for your really nice comment, I'm glad you liked the tutorial and found it useful! :)
Thanks for tutorials, Your RTS ones are so useful too.
Thank you so much! I'm really glad it can be of use and that you find the RTS series interesting ;)
Excellent tutorial, and textbook code :)
@Darryl JF Hi! For some reason, your comment doesn't show over here... sorry :/
But thanks a lot, and you're right about the size of the text - I'll try and improve that in my future videos :)
Nice short tutorial. I think ill make something similar but using Scriptable objects. To simply drag and drop on every different ai.
I'm happy you like the video, thanks for your nice comment!
Yep Scriptable Objects can be a nice way of composing behaviours - have fun playing around with that :)
@Mina Pêcheux I'm loving this tutorial! I'll be coming back to gradually soak up all the information and, if you're cool with it, I'll drop some questions that may arise when building my own system. Thanks! :)
I'm really happy you like it, thanks so much for your nice feedback!
And yep - be sure to ask if you have questions, hopefully I'll be able to help ;)
Cheers!
Hi Mina, are behaviour trees recommended for Player/Characters or just for AI? In which case I imagine one would have to handle player/characters with something else, such as hierarchical state machines.
My problem is the following: I was working with Visual Scripting and I made a complex system of my own using superstates inside a FSM, and it works really great. Now, I was trying to translate it to c#, so I started learning about State Machines, hierarchical FSMs and behavior trees. Working with such complex tools and systems is easy in Visual Scripting, and by that time, expanding the system felt natural even without knowing the exact tools I was implementing.
But now that I look at it, I can't find an exact description of that system. I think it's a mixed of a Hierarchical State Machine with multiple and Concurrent State machines - meaning that in my Base Superstate I have multiple superstates running at the same time and also have other "regular" states inactive and waiting to be transition to, and each supertate has either other superstates (with either more superstates or just sub-states and so on) or sub-states, and also there exists superstates with multiple sub-states that are active at the same time, while other sub-states remain inactive (and are waiting to be transition to to).
I hope the explanation makes a little sense at least. Would you say this system would be more similar to a behavior tree than a hierarchical state machine? I'm asking this because as of right now I still didn't dive so much into behavior trees, so I'm not sure (I'm still on the surface).
Thank you for reading!
@@guille_sanchez Hello!
So, in theory, there is no one-to-one match: you can use FSMs or BTs for AIs or Players. Usually, the way you choose between the two is that:
- FSMs are "simpler" than BTs: they rely on more scoped states with a clear beginning and end
- if you have "superstates" (and assuming you're talking about some states that regroup others?), you can either keep FSMs but indeed look into Hierarchical FSMs (I've made another tutorial on this topic here :) ruclips.net/video/OtUKsjPWzO8/видео.html), or switch to BTs
- hierarchical FSMs are good to keep your code readable and easy, but they can be limited because your states are still quite tightly coupled, so you end up having a lot of shared variables and global data
- BTs are usually more "modular": you can start with just a brick of behaviour and then add more step by step as you connect additional checks and tasks
The thing that would lean towards BTs in my opinion in your description is the "multiple and concurrent state machines" + "multiple sub-states that are active at the same time" part: as far as I know, in a "normal" implementation of a state machine, you implicitly assume that you have juste one current state, and you use just one state machine on an agent. It's totally doable to have several current states in terms of coding, but it's really not the standard and can quickly give you a big headache since you'll always have to stop and think when "accessing your Player's state" (which one are you referring to?...).
BTs are built on this "multiple states" concept since they can handle parallel branches. Basically, depending on the type of flow node you put above your branches, you decide whether the behaviour inside each should run at the same time or in an exclusive way.
Also: even though I never worked on that, I have less trouble imagining a game object with multiple BTs than one with multiple state machines. The whole point of FSMs is to centralise behaviour in one place and have your machine manage everything more or less in a bubble. BTs on the other hand can be a bit more "specialised", since they are modular: instead of having just one big BT with hundreds and branches and hard-to-pick flow nodes, you could simply create several BTs that each represent a (coherent and autonomous) part of your full behaviour.
I think BTs would be easier to translate to given your current architecture... but I can't be 100% certain since it's just theory ;)
I hope it will still be helpful,
feel free to comment again (or send me an email at mina.pecheux@gmail.com) if you want to discuss this further :)
Cheers!
@@minapecheux Thank you for the very nice and detail explanation! I think you got my point pretty well and I pretty much agree with your assessment. After having some thoughts on it, I think for now I'm going to stick with the hierarchical state machine for the player (and probably add to it some other state machine that can be active at the same time (concurrently)). This is just because I saw other people implementing it (as yourself, thanks for the link!) and I can consult those or get some references if needed. However, I don't discard the possibility of upgrading or reworking the player's script to a behavior tree in the future (for now I'll use it for enemy AI).
And by the way, I think it'd be really cool seeing somebody showing a player controller (2D platformer or whatever) with a behavior tree implementation, I don't know of anyone who has ever done that.
@@guille_sanchez I'm happy I could help! Best of luck for your project and AI-learnings :)
Hmm, that's right. This could be a great idea for a follow-up on this episode... I will think about it, thanks!!
That's extremely helpful, great voice, great explanation, great tutorial. Thank you.
Hi! Thank you so much - I'm really glad you like the tutorial :)
@@minapecheux i just have few questions. Can you please explain: 1
@@tarkerro So:
1. the 1
Great tutorial! Thanks from China!
It's too much for me, but I understood something. Excellent video!
Thanks, I'm glad you liked it!
Wonderful tutorial, very clear explanation 👍
Hey - I'm really glad you liked the video and found it useful!
Thanks a lot for your nice comment :)
Thanks for the great tutorial! It's been quite helpful.
I do have one question in regards to the way the selector and sequencer works. So, right now I have an object (or respectively the skeleton from your case) and if this object moves out of range from the "Guard", then shouldn't the "Guard" return to it's patrolling behaviour?
Right now for me the guard just chases the "Skeleton" endlessly into infinity and I can't seem to find a way to have it return to patrolling when the "Skeleton" escapes the guards range. I'm assuming that when the sequencer starts an action, it'll pursue this until it's completed, which would work fine in most cases but It'd be nice to have some variety where entities will stop performing a task if they're outside a given range. Any Idea on how this can be solved?
You're welcome, I'm glad it helped :)
Indeed, if the skeleton gets out of sight, the guard should automatically switch back to its base patrolling state because behaviour trees are re-evaluated continuously (so you shouldn't be "blocked" in a sequence, the tree would actually re-evaluate from the top and either get to the exact same point in this sequence if the situation is the same, or move on to another part of the tree if it's not)...
What's your setup? :)
Have you simply put the same AI logic on the skeleton as well? Cause for now my two ideas of potential issues could be:
- if you've used the same AI: the two characters have the same speed, and so they can never escape each other's field of vision
- if you've used another AI: your check for "inside the FOV" isn't working as intended...
If you want, you can send me your project via email (at mina.pecheux@gmail.com) and I'd be happy to try and help you debug ;)
Hope it helps,
Cheers!
@@minapecheux Thanks for the quick response! For the entity that's the equivalent to your skeleton, I have added a simple movement script merely to test this behaviour. Regardless of the speed on each entity, the "Guard" will chase the "Skeleton" endlessly. I'll send over the code if you have the time so you can see it more clearly. Thanks again for all the help! c:
Thank you so so much for this video, exactly what i needed
Oh wow how are you not having millions subs? Here's one from mine. Thanks a lot for the great tutorial!
Hi - thank you so much! I'm really happy you liked the video (and thanks for the sub ;) ).
Cheers :)
Great tutorial. You should have a million views! 😁 👍
Haha thanks, I'm really happy you liked it! :)
Great guide and explanation. Always wondered how to interrupt a state while doing something. One thing i didnt like is the parent.parent approach since we need to know which node we are in, might be a complex/non-scaleable approach if we have lots of nodes. Instead, couldnt we pass "GuardBT" variable and share the data from there?
Thanks for the kind comment! Yup I totally agree this is not the greatest - my idea was to keep the whole thing as "self-contained" as possible, but it's not very handy here... feel free to adapt this to your liking and improve it for your specific use case ;)
Cheers!
Great tutorial, thanks for your amazing work. 👍
And thanks for your nice comment - I'm really glad you liked the video! :)
You teach very well, I really like your explanation :D
You have a new sub!!!
Thank you so much - I'm really happy you like the tutorial :)
(And thanks for the sub ^^)
Hi, one advise.
For next videos try to zoom in a few time so it can be easier to read in smaller screens!
Hey! Yep, that's good advice: I'm doing it on my more recent videos now :)
Thanks!
Hello Mina! Thanks for this awesome tutorial. Straight to the point. Although I have a doubt, probably a misconception... In the SEQUENCE node, shouldn't a RUNNING state prevent the next state from evaluating? That's not whats happening with this implementation. It's driving me nuts lol. I changed the code in the sequence node to return the state when RUNNING and now it's working as (I) expected. Could you please explain to me whats wrong? Thanks!
Hi - I'm happy you liked the tutorial, thanks for the nice comment :)
So there are actually various ways of implementing the Sequence or Selector nodes, exactly because of those subtle differences: depending on the articles I read, some people preferred for the "running" state to be a pass-through (like here) or a blocking step (like in your implementation): both are "ok" because there is no absolute consensus . Therefore it's mainly up to your needs and what you want to code in your project ;)
I personally feel like "running" is still undecided, thus it shouldn't block this entire branch upfront, but you can feel differently!
I hope it helps :)
cheers!
@@minapecheux After I watched the video I came down here to ask the same question and found Gabriel did, thanks both of you !
@Gabriel, I am with you on this topic. @Mina's sequence can run multiple actions simultaneously, thus a better name for it would be Parallel. Considering a simple scenario of typical sequence with several actions: "Walk to a door" and "Open a door". The later shouldn't run until first one is completed.
Thanks a lot for the clear tutorial, I learned a lot!
Glad to hear that, thank you for the nice comment! :)
New fan here.. thanks for the great tutorials! Subscribed
Hello - and thanks for your nice comment and your subscription, I'm really happy you liked the video and are enjoying the channel :)
Cheers!
So I really like your tutorials on Unity, and I was wondering, as design patterns are really powerful tools, would you recommend like a good starter kit ? FSM and Behaviour trees, something more ?
Thank you for incredible work !
Many thanks for your nice comment, I'm glad you like my content! :)
About design patterns, I guess the hard part is that you ideally need to have a basic notion of most of them to properly assess which one is the most suited for your use case; I'd suggest you read this amazing book available totally for free on his website, by Bob Nystrom, called Game Programming Patterns: gameprogrammingpatterns.com/
It's seriously one of my "woah-reads", that really helped me understand the breadth of the topic!
Hope it helps,
Cheers ;)
Thank you, I'll do so !@@minapecheux
Thanks a lot for the clear tutorial, I learned a lot! I have a question. If a child node of a Sequence node is in the RUNNING state, it should wait for this child node to run and return to the SUCCESS state before the next child node can run. Because if the running result of this child node is FAILURE, the next child node should not run.I don't know if I understand it this way, right?
Heya and thanks for the comment - I'm glad the tutorial was helpful! :)
Yup, a Sequence node basically proceeds while the children return a SUCCESS! Usually, if you need to have multiple RUNNING children execute at the same time, you use a Parallel node, so there's no need to duplicate the behaviour in a Sequence ;)
Hope it clears things out,
cheers!
@@minapecheux If you agree with my statement, should the Evaluate method in the Sequence class be written as follows:
Case NodeState RUNNING:
State=NodeState RUNNING;
Return state;
Because we should wait for the nodes in the RUNNING state to finish running and return SUCCESS before running the next node.
Am I correct in understanding this way?
Heya, sorry I read your comment to quickly before ^^@@jackjia5083
So from what I've seen, there are different ways of implementing behaviour trees and you may stop or not a Sequence when evaluating - I think the important part is that if at least one child node is running, then the Sequence is running too.
But again, I believe there are various possibilities here, so depending on your use case, you can totally adapt it and exit early! :)
Hope it helps,
cheers!
Really good explained and exampled, though I miss a review example of flow at the end.
Thanks, I'm glad you liked the tutorial! Just so I'm clear, what do you mean by an "example of flow"? :)
@@minapecheux like a example of the execution flow, in the behavior tree. Then the character move he goes from patrol to detecting enemy and chasing him. Like with arrow in the chart. Sorry if not been clear. In general really good video overall.
@@lovePotato No worries ;) So actually I plan on making another tutorial on how to create a visualizer for a behaviour tree at runtime, so I guess I'll show this kind of execution flow at that moment! But I'll definitely keep that in mind, thanks :)
Thanks for sharing!
Awesome
I'm happy you like it, thanks for the nice comment :)
very easy to understand, keep up the good work!!!
Glad it helped, thanks for the nice comment!
Wow. amazing job! Subbed!
Thanks for your nice comment - I'm really glad you like it (and that you subscribed ^^) :)
Thank you for this good tutorial.
If you can please change the presentation and IDE to a dark theme, it might be easier on the eyes (creator and viewers alike...)
I'm happy you liked the tutorial! :)
I've updated my video visuals since then and I'm now making sure to use a dark theme in the IDE screenshots :)
Hi Mina, first of all thank you for the comprehensive tutorial. I have a question, how would you add an inverter to your behaviour tree?
Thank you for your nice feedback, I'm happy you liked the video :)
So to make an inverter you basically need to return a success if the child evaluates as a failure, and vice-versa. If you want to see an "upgraded"/extended version of the BehaviorTree package, you can take a look at my new Github repo for my Unity tutorials over here: github.com/MinaPecheux/unity-tutorials
The tuto n°1 actually goes further on behaviour trees and shows how to use this advanced package to create an RTS collector AI ;)
I hope it helps,
cheers!
Thank you very much, I was actually going through your RTS project as we speak. Thanks again and have a great day :)
Great tutorial. Please zoom in on the code more next time
Thank you for your nice comment!
Yep, sorry - this was an "old" tuto, and since then I've made sure to apply this precious advice :)
But thanks for pointing it out!
Cheers ;)
Love this tutorial, especially the text version linked in the description. Took me a minute to fully understand the concepts but you’ve done a great job getting me to the point where I can implement this my self.
The only big question I have is when we use the SetData() for storing our target. Is there a different way to SetData to the root without using parent.parent? With parent.parent I won’t be able to use this node at any other depth in the tree right?
Hi - glad it was helpful! :)
About your question: it is indeed possible to reference the root more directly in each node, so that you don't have to "climb up" the hierarchy like this. (Basically, it's interesting to do this climb if you want your node to work in a standalone subtree that is packaged and reimported, thus self-autonomous - but using the root is way more handy in most "normal cases" ^^)
If you're curious to see how to do that, I've actually added this feature in a new version of this lib available on Github:
- in the repo of my ebook on AI Programming: github.com/MinaPecheux/Ebook-Unity-AIProgramming
- or in the repo of my new (modern) Unity tutorials: github.com/MinaPecheux/unity-tutorials
Hope it helps, cheers! :)
Hey, thanks for the tutorial, i have a few questions :
- Why did you not pass more data through the constructor, like the speed or animator ?
- For a sequence, it is possible to have many checks and multiples tasks ? Or it is better to decoupled them by creating new children ?
- Is a behavior tree only deterministic, or it can be stochastic ?
Hi - thanks I'm happy you liked the video :)
So about your questions:
- we could absolutely pass this info through the constructor but we can either retrieve this data from the transform (with the GetComponent) or from the BT class (with static variables) => "automating"/centralising the info is a way of reducing the risk of human errors ;)
- a node can be as complex as you want and it can contain multiple checks or tasks - but it's usually interesting to have pretty "simple" nodes because it makes the entire system more modular
- a BT can totally be stochastic: you can have some randomness in your control-flow nodes (ie the Selector or Sequence) so that the children are run in a random order
Hope it helps - feel free to post again if you have other questions!
Cheers ;)
@@minapecheux Ok thanks for your answer, for my second question, for example, if i want to check if a enemy is in range, classic check, but for example it's a boss, so i want him to have multiple melee attack type, what do i have to do, create 1 script with multiple attack pattern, or separate each different attack ? If separate, do i have to copy another sequence, add some randomness so maybe he'll check among a possible attack with same check ?
Or maybe another solution is after the check, create a new sequence with 2 tasks (attack 1 and attack 2) and call the sequence after the check, then the sequence randomly choose a task ?
@@Sky1Max Hey! So, there is no absolute "right" answer, but in general the more you separate your behaviours, the best it is because it makes your system more modular: if you suddenly decide that you want sub-bosses that have some of the special checks but not all of them, you can easily re-inject this little bit of logic in their BT without hassle! :)
Adding randomness as some advantages: it makes the behaviour more "human-like" because the players can't always guess the next move... but is also a drawback: the players can't guess the next move! ^^ What I mean that for games like roguelites/roguelikes that rely a lot on mastery, it can be a bad idea because players expect to learn the patterns of your enemies - so randomness can collide with these genres.
But in terms of implementation/structure, I'd go with your 2nd solution (even if I don't know the specifics of your architecture here) because it looks to me like it would be the most "efficient" -> you only do the check once, and then have a special "random sequence" that has this randomness auto-integrated and is tuned to handle this situation.
I hope it's clear, again don't hesitate to ask follow-up questions! Cheers :)
Great tutorial! Thanks a lot!
Hi, and thank you for your nice comment, I'm really happy you like it!
Cheers :)
Thanks for the tutorial, Mina.
I'm wondering why _dataContext is in the Node class; why not put _dataContext in the Tree class? Then we don't have to do recursive searches.
You're welcome, I'm happy you liked it :)
So the _dataContext could totally be "extracted" to the tree, or the root node; I put it their to have a fully "modular" structure where any node could potentially become the root of a new subtree instantly, and to properly encapsulate the data, but you're right: we could absolutely "specialise"/improve the scripts to better suit the situation and increase the performance!
I hope it helps :),
Cheers!
@@minapecheux Thanks for answering 😉
Amazing tutorial !
Just one thing, at 17:40, you use parent.parent to access SetData method, will it not be simplier if every node has a reference to the root in addition to its parent ? In big tree it can quickly became messy
Thanks, glad you liked it! :)
Yup, you're totally right: depending on the size of your tree, it can be nice to use the root. Here, I relied on the direct hierarchy to be as modular as possible, but this is definitely a pretty specific example case...
Hope it clears things out,
cheers :)
Hi, thank you for the nice and lightweight starter code.
I was wondering something. BT's ask for a return state. In your example the patrol, attack and goTarget actions only allow RUNNING as a return state. I see that the example works fine, but isn't it a bit of a hack, since the possible FAILURE state is provided by the predecessor node, where a SUCCESS state is never achieved. Therefore the provided GuardBT will never return SUCCESS.
My point is that, in my view, this "kind of" violates the modularity of BT's since if someone would want to extend the behavior with an additional node at the end with either a sequence of selector it will never be reached.
Although BT's heavily rely on the order the block's are placed I think it should not lock out following blocks.
What are your thoughts about this?
Hello, thank you for your nice and interesting comment, I'm happy you liked the tutorial!
You make a very good point: it is true that in full generality you should try to "isolate" your actions in autonomous subtrees while making it easy to put them together into a larger tree.
However, the RUNNING state can be an acceptable state, too :)
The flow of your logic depends on your flow-control nodes - what I mean is that it's how you define your Selector and Sequence that you determine if the RUNNING state is blocking or not. And just a little precision - usually, Selectors and Sequences are blocking in some way (either at the first success, or at the first failure for example); it's the Parallels that don't "lock out" the following blocks (if I understand properly your remark) :)
But also, to be honest: in this video, I didn't want to spend too much time covering modularity cause it was already quite long, and it's totally possible this got a bit fuzzy in my explanation...
In any case: thanks a lot for your question/reflexion, I think it can benefit the other readers as well so it's really cool ;)
Cheers!
Question : Why are you not defining the node class as abstract class?
Hey, you're absolutely true, it would make more sense! ^^
In truth, I initially wanted to let people free of optionally creating their own nodes from scratch, but then edited this out because the tutorial was too long, and then forgot to redo the beginning to adjust for this.
But yep, in this setup, you would totally make the Node class abstract to avoid people instantiating it themselves!
Thanks for pointing this out :)
Hello, thank you for this helpful tutorial. I'm still a little confused about the "running" state. For example, imagine a sequence node gets evaluated during a frame. After all of its other children return "success", its rightmost child returns "running", which causes "running" to be propagated back up to the root, ending the evaluation that frame. Then, on the next frame, that same sequence node ends up having to evaluate again. Will it evaluate all of its children normally from left to right, or begin by evaluating its rightmost child because it returned "running" in the last frame? Intuitively, I would think it's the latter because you would have to continue running that last child since it's not finished, but maybe I'm missing something about how behavior trees work in general. Also please tell me if anything I described is wrong.
Hi, and thanks for your nice feedback! So with this basic BT implementation, you indeed re rub all possible nodes every time.
It's obviously not optimal and not efficient, and real game studios usually have a bit of extra "state logic" to shortcut to the right place and avoid it. But as is, as I only wanted to demonstrate the core principles of the tool, you'd have to re evaluate the nodes, yep ;)
Really well structured tutorial! I should be able to start implementing this into my combat game asap!
Just a quick question, could you briefly break down in what situations you'd use a sequencer node vs a selector node? I had some trouble visualizing AND and OR gates in the context of nodes
Thanks!
Hello - thanks a lot! I'm really happy you liked the tutorial and found it useful :)
About the AND and OR: it's basically about the order of execution, and about whether or not you want the other children to run after one failed.
If you use a sequence (=AND), then as soon as one node fails, the entire branch is "dead" and will abort. This is typically interesting when your branch consists in one "check node" and one "task node" (like the "CheckEnemyInFOVRange"/"GoToTarget" duo here).
If you use a selector (=OR), then you can have a child that fails, and the tree will continue to try the next children. In other words it's only if all children failed that you failed. This can be useful to get an idle behaviour: you have your "main" branches on the left (=with a higher execution order) that implement your real behaviours like watching out for targets or attacking, and then if all of them fail you end up in your idle branch on the right.
I hope it's clear, feel free to tell me if you have other questions/are still confused!
Cheers ;)
@@minapecheux Yes, ok! This makes things a bit clearer as to why you organized Sequence nodes under Selector nodes in your example--Selector continues the run the TaskPatrol node even if the Sequence nodes return failure. I think I get it :]
@@minapecheux Another question... Finite State Machines typically have a "SetState" method that determines precisely what state the statemachine is in. What would be the equivalent of that in this implementation of a behavior tree? Would that be the state = NodeState.SUCCESS statements?
Because something I liked about FSM is that when the states switch, you can call "OnEnter" and "OnExit" for each state respectively like so:
public void SetState(IState state)
{
if(_currentState == state)
return;
//do things while leaving the state
_currentState?.OnExit();
//swap state to a new state
_currentState = state;
//do things while entering new state
_currentState.OnEnter();
}
The advantage to this is that you can, for example, turn off a boolean in the animator over the course of 1 single frame. I'm worried that the Evaluate function cannot perform the same way.
Please let me know what you think.
Thanks!
@@cyprusplotarmorstudios4396 Hi! So the whole point of behaviour trees is to be a bit more "flexible" than state machines - in particular, you don't actually encode a precise state to enter and exit, but rather you compose it by adding up the reachable nodes in the current context.
I don't know if I'm clear ^^ but in short what I mean is that to re-create the OnEnter and OnExit you have to integrate this logic in the structure of your tree itself (for example with some check nodes for the "first frame" and "last frame" of your "state" that sporadically run some specific sub-branches in your tree).
I hope it helps, tell me if it's not clear! :)
Amazing tutorial! Very well structured and understandable!.
A few questions:
-I noticed that once the AI goes into the branches it stays there/loops inside. Is there a way to make it loop back to the top every few seconds?
- Also I'm not sure I understood, If I want the check node to go to a specific action node on success how do I call it there?
To give perspective, in the project I'm making the AI has 3 options he needs to choose from every 3 seconds, one of the 3 options is dependent on a specific option. The third is always available.
And the game loops until one win
Thanks a lot for your nice comment, I'm really glad you liked the video!
About your questions:
- actually, every frame, the full tree is executed from the root (in the Update() function, you see that you call the logic on the root node): it's just that, most of the time, you're going to end up in one of your branches again and again because all the checks give the same result while the overall situation is similar. Therefore, you feel like the AI just "stays" in the branch - even if, in truth, it's simply re-traversing down to this branch every time :)
- the idea is that "transitions" between your nodes (or perhaps more accurately "dependencies") are set by how you define the architecture of the tree: depending on whether you use a Sequence ("AND" node) or a Selector ("OR" node) for example, you can determine the sibling node that should go after a check
Oftentimes, it's actually easier to start from the bottom (the leaves of the tree) and then work you way up when you design the architecture. For this precise example, you could think of it this way:
1. I place a "Check" node on the left of my diagram because I want it to be run first (and convention dictates that left = higher priority)
2. if it succeeds, then I should run my "Action" node; else, it should be stopped from executing => I want to have an "AND" parent node above those two nodes (this way, if the first "Check" node fails, the second one won't even be tried)
3. if you want an "else" case: suppose you have an "Action 2" node to run if your "Check" node fails. For now, you have a little branch of 3 nodes already designed: the parent Sequence and its two children, "Check" and "Action".
All that's left to do is add yet another parent node above this branch and your "Action 2" node - except this time you want a Selector ("OR" node) => this means that:
> the first child of the Selector (= your branch of 3 nodes) will be executed => if it succeeds, the Selector is done and doesn't need to execute the second child (= "Action 2" node)
> else if the first child fails (because the "Check" fails) => the Selector tries to run the second child
(3b. to have a third option that always plays, you should replace the top Selector node with a Parallel node - more info about this composite nodes here if you want: mina-pecheux.medium.com/making-a-rts-game-23-implementing-behaviour-trees-for-our-units-1-3-unity-c-1a61840058a6)
Hope it helps,
cheers! :)
@@minapecheux Whoa Amazing! This was spot on the extra info I needed!. Thanks a lot, Mina, Keep up the amazing work!
Very good tutorial. In Tree.cs, why is Start() protected while Update() is private? Will Update() execute every frame on scripts that inherit Tree (like GuardBT) even if Update is private?
Thanks, I'm glad you liked it!
So, in C# (and most OOP languages I believe in fact), your derived classes will automatically "copy back" the logic of their parent's - but they can't access it. So if you need to call the method you inherited, or get/set the variable you inherited, you have to make it protected - this way, it becomes accessible to any instance of the parent class, AND any instance of a derived class :)
In the case Update(), Unity takes care of calling it for us, and since we don't override it in any way, we don't need to call the base logic - so we don't need to access it ourselves.
Hope it helps,
cheers!
@@minapecheux Thank you for your clear explanation. I understand now.
Nice tutorial, helped me with the basics of understanding the setup of a behavior tree. But technically, the code is not correct. When a leaf returns "running" the selector or sequencer is supposed to evaluate it again until it returns "Success" or "Failure". In the case of the video, it starts with the first child again in the foreach loop. A simple fix is to keep track of which child is being ticked / evaluated in the selector or sequencer if it returns running and continue from there.
Wonderfully helpful! I had one question though. What's the best way to remove a node from a tree? For example, I have a spawn node that all characters enter initially, but once exited can never re-enter. If I wanted to get rid of the spawn node so that that guaranteed fail evaluation doesn't happen, is there a good way to approach that? Thanks again for a incredibly educational channel!
Hi - and thanks for the nice comment, I'm glad you liked the tutorial :)
So about your question: I guess you'd have two options:
- either you store a field in your root's data to say "I've been spawned now", and you add a condition on the Spawn node to avoid this branch if the "already spawned" data field is present
- or you indeed actually remove the node from the tree; depending on the current structure of the tree, this would be done by simply removing the node from the children list of its parent node (if it's a leaf in the tree), and/or by also reparenting its sub-branch to the node above (if it's in the middle of the tree).
I hope it helps, feel free to ask if it's still not clear! :D
Wonderful video! I have one question; why do you make the sequence continue evaluating if there is a RUNNING action?
Thank you so much, I'm really happy you liked the tutorial! :)
So it can change a bit from one behaviour tree (BT) implementation to another but, in the sources I found and learnt BTs from, Sequences were only interrupted in case of failure...
Perhaps in your case you should also stop if a child is running, not sure - it can depend on your specific example ;)
@@minapecheux Ah okay that makes sense, thank you! Just started reading about this stuff today so I wasn't sure whether this was a standard or not 😅 Really excellent video, hope your channel blows up at some point! (Figuratively speaking of course!)
@@agirmani Haha, thanks a lot for the support :D
Yep it's a complex topic and to be honest, I feel like it can adapted in quite a lot of different ways, so I think keeping an open mind and asking yourself 'why this and not that?' is very positive, here!
Feel free to come back and ask other questions, on this video or another, if you want ;)
Cheers!
great tutorial! I have a question, can i apply this kind of behavior tree in visual novels, for dialogue and endings branches?
Thanks, I'm glad you liked it!
I think so (although to be honest I've never tried it) :)
After all, dialogue trees use a similar structure to behaviour trees, and you could use the behaviour tree 'action nodes' to actually output a dialogue I guess...
Hope it helps,
cheers!
Thanks for the article on medium. Really well written!
Can you please recommend some resources which can be helpful to turn this BT into a Visual Editor in Unity?
Thank you for your comment, I'm happy you enjoyed the tutorial :)
About your question: do you mean in-game or in editor behaviour trees?
- if you're interested in in-editor tree editing, I devoted the very end of my Unity RTS/C# tutorial series to this topic (it was for a technology tree, but the idea is the same :)): mina-pecheux.medium.com/making-a-rts-game-50-implementing-a-technology-tree-1-3-unity-c-1c516ba78712
- for an in-game display: you could use a similar placement technique as the one I discuss in the link above; but you'd probably use the Unity canvas-based UI system, so you'd apply this positioning to Images, and Texts, and all...
(- bonus: for an in-game "debug" display: you can also take a look at the Unity old GUI system that makes "poorer" UIs but is quite quick to implement: docs.unity3d.com/Manual/GUIScriptingGuide.html
I hope it helps a bit! :)
@@minapecheux Hey, Thanks for the links!
I needed an in-editor Tree, to facilitate the debugging on the State for BT AI.
I will surely check out the resources you shared and would let you know.
Super tuto :)
Thanks! :)
The tutorial is awesome. I'm wondering if it is possible to add a message box between the task and also an interruption decorator?
Hello, and thanks for the nice comment! :)
So, sorry, I'm not sure exactly what you mean by "message box"? Do you mean printing something on screen?
As for the interruption decorator: the idea of this behaviour tree logic is that a specific branch executes only when the condition it contains are ok. Therefore, to "interrupt" a task, you basically need to switch of the flags and variables that make up your context in order to prevent the next cycle from taking this route in the tree structure. There is no real interruption decorator per se, it's more about blocking the flow...
Hope it makes sense ;)
Cheers!
Great video!
Thanks!
Why would you use the SetData / GetData methods instead of exposing some variables in the GuardBT class and passing the GuardBT reference through the constructor?
Hi! So the idea is to try and abstract away all the logic/functions that are common to any behaviour tree in the BehaviorTree package, and in particular in the Node parent class. Also, keeping the data at the node level allows you to create more modular (sub)trees to then re-use and re-assemble in various ways ;)
Really good tutorial. Nice job! I am building a different type of game from the on in the tutorial, Its more of a virtual pet game. But your tutorial was very useful. I was wondering, in the last step when you where implementing the attack part, is there a way to make the sprite return to following the waypoints without destroying the object? (because in my game, its TaskBreed not TaskAttack, and so none of the sprite will die)
Thanks for the comment, I'm glad the video was useful :)
Basically:
- if your TaskBreed node is in a Sequence branch and depends on a check node, then you need to see what conditions are checked for running your TaskBreed node, and make sure those conditions are currently valid to have the node be executed
- else if the TaskBreed is a fallback node (so on the right of the child nodes list), you have to make sure that no other branches gets activated and takes precedence
I hope it helps you a bit, feel free to tell me if you want more pointers! (either here or by email, at: mina.pecheux@gmail.com)
Cheers :)
@@minapecheux Thanks for your reply. I'll try it out 😁😁
Hi Mina,
For the data context, wouldn't it be better for it to be static since we're gonna be checking the entire tree anyways?
thank you :)
Hello! There are indeed many ways of handling the data :)
Here my goal was to make each node as self contained as possible, so that you could "cut your tree into subtrees" wherever you wanted...
... but it might not be the most efficient in a lot of cases, and you're right that oftentimes we rather have a global data storage for the entire tree called blackboard:)
(Actually, I made this change in a more recent version of Unity behavior trees, which you can find on my Github: github.com/MinaPecheux/unity-quick-tutorials)
Hope it helps!
Cheers :)
Hi! Thanks for making these awesome tutorials!
I have a beginner question. Starting at 9:33, why do you need to set "parent = null;" in the first constructor, but not in the second constructor?
Thanks, glad you like the videos! :)
Hmm... it's actually a little forgot bit, I believe, sorry ^^
In fact, the parent should be initialised to null, so it should do the same in the end... but you're right, we could explicitly set "parent = null" in the other constructor too!
@@minapecheux Oh, I didn't expect you to reply right away. Thanks a lot for answering!
17:40 At line 26 -29 you directly set it to parent. parent cause that's the root. But what if you didn't know the root's exact number upwards?
Wouldn't it be better just in case to make a loop that goes up the tree until there's no more parents, then sets the data at the last parent?
Hey!
So one of the good things with behaviour trees is that they can be modular and fairly self-contained... typically it can be a good idea to encapsulate the data that is related to a specific part of the behaviour to this part of the tree :)
But it's just a quick idea here - you could also decide to compute a root once and for all, and tell all the nodes beneath it to consider this one as the root.
I hope it helps,
cheers! :)
Hey its a really wonderful tuto ! I was wondering if its possible to make FixedUpdate or LateUpdate work with it ?
Thank you so much, I'm glad you found the tutorial useful! :)
I think it should work - you could either simply have the entire tree tick happen in another update hook, or give your nodes multiple update methods, and call the proper one in each *Update() method from your BTree class (and propagate this *Update() method call to the children)...
Haven't tested it, but I don't see why not ^^
Hope it helps,
cheers! :)
thanks!
Hi Mina, first of all very nice tutorial about behavior tree, and may I ask a few questions. I'm new to behavior tree and I read some articles about it and there is a node called 'Decorator', I didn't see any decorator defined in your video so I wonder if u can make a video about how to implement these too.
Also I wonder how's the performance of this because tree is calling root.evaluate every update, so if unit number is large and the tree has a lot nodes, will it cause performance issue?
Finally, subscribed your channel!
Hello - thanks for your comment, I'm happy you liked it! :)
So the 'Decorator' node in behaviour trees is not an actual node in itself, it is a category of flow-control nodes. As I say in the video, all the inner nodes of the tree control the flow, but there are 2 types: the composites and the decorators.
Composites have multiple children (eg the Selector or the Sequence) while decorators have only one (eg a "NOT" operator to reverse the success/failure of the child or a Timer that executes a given action every X seconds).
In other words: the Decorator class in itself doesn't really exist, rather we usually implement a specific Decorator (for example the "NOT" or the Timer). If you want to see trees that are a bit more complex and that use some decorators, you can check out the RTS tutorial I did and in particular episodes 23 to 25 :)
mina-pecheux.medium.com/making-a-rts-game-23-implementing-behaviour-trees-for-our-units-1-3-unity-c-1a61840058a6
I hope it helps - cheers ;)
About performance : Evaluate only proceeds on viable branches and abandons branches that are not viable so (correct me if I'm wrong): Runtime complexity is approximately O(N Log N) which is not too bad.
I'm experimenting with this system. One question I have, is the sequence node supposed to play all the running child nodes simultaneously? From what I understand it's supposed to play them in order one after another.
Hello!
So from what I've read, it depends on your implementation; both exist. In my system, I've voluntarily chosen to block the execution only if the child node fails, but you can totally block the execution if they're running, too. It depends on what you want :)
Note however that, most of the time, if you want multiple behaviours to run at the same time, you don't use a Sequence but rather a Parallel composite node ;)
Hope it helps,
cheers!
I'm confused why the dictionary is not shared between all tree nodes instead of each node having its own. It seems really inefficient to access different dictionaries to find one variable or I'm missing something.
Hello! So one big advantage of behaviour trees is that they are very modular. This means that, in theory, your final tree can be composed of several subtrees that each handle a specific task... and are each autonomous! Therefore to be totally generic, I showed a version where any node can be the root of a specific independent subtree, and you can keep the data local to this part of the tree.
But of course depending on your use case, it can be valuable to simplify/modify this and have just one big pool of data to refer to :)
I hope it helps,
cheers!
super helpful ty :)
You're welcome, I'm glad it was helpful! :)
Thank you for the tutorial!
I had a question though, what if one of the behaviours requires a coroutine to complete it's functionality?
How would I handle the return statement here for the Evaluate method? And since the behaviour inherits from the Node class, I can't use coroutines to inside them. Is there a solution to this?
Hi! Thanks for the tutorial, best one I've ever seen of behaviour tree in unity. But I had a problem while following it, I had a problem with the patrol part of the Behaviour Tree. I followed the previous Patrol AI video of yours and it works but the patrol system in the behaviour tree doesn't, which doesn't make sense to me since they are the same. I thought this could be a problem with my animations, rigs, and so on, but everything was right. The AI is simply ignoring the waypoints. Any idea of what the error may be? Thanks in advance
Hey! Thanks for your feedback, I'm glad the tutorial was useful :)
Hmm, just in case: did you assign the waypoints properly in the inspector? Or else, perhaps you can put some debugs at various points to understand where the logic "breaks"?
Also, if you want me to take a look, you can send me your code via email at: mina.pecheux@gmail.com.
Hope it helps,
cheers :)
thanks for the answer! I set up the waypoints correctly, but didn't test debugging them. I'll do that and let you know.
Très clair, merci. Le seul truc qui me gêne sont les appels à GetComponent dans les 2 nodes Check et dans le node TaskPatrol. J'aurais plutôt fait cela dans GuardBT, et ajouté l'animator dans les paramètres des constructeurs. Bien sûr, ce ne sont pas 2 appels supplémentaires qui vont ruiner les performances, c'est juste de rester sec. Pardon. DRY.
Unity étant revenu sur ses FEE, revenez-vous sur votre décision de ne plus faire de tutos ?
Merci pour le gentil commentaire, et content que la vidéo vous plaise :)
Ouip, on pourrait clairement améliorer la separation of concerns - c'est vrai que j'ai essayé de trouver la bonne balance entre "faire un truc clair et rapidement explicable" et "quand même coder quelque chose d'efficace et bien rangé"... pas toujours simple de choisir sur certains points ^^
Pour l'instant, même si je trouve que la lettre ouverte de Unity à la communauté est un grand pas en avant, je ne prévois pas de revenir aux tutos Unity - en gros, parce que ça a été pour moi un bon motivateur pour (enfin ?) passer à une engine open-source, plus en accord avec mes principes de partage et de gratuité :)
Nice tutorial. A few caveats. In Sequence:Node in case RUNNING should be return with RUNNING, not continue to next Node. Visual part (animation) should be separated from behavior. Dictionary of wrapped objects create garbage. Some other small things. I have different problem. I have to run node asynchronous from Evaluation(). But only one should be running at any given time. Not sure how to do it yet. I have some naive ideas (like adding Stop() method and storing last .Running node) but it's getting complicated and kind of dirty.
Heya!
Thanks for your comment and for your feedback :)
About your question on async evaluation methods - I haven't tested it myself but I believe there are two possible ways of handling that:
- if you need a real async function, you can use either a C# async Task or a Unity Coroutine, and run it from the parent MonoBehaviour script; and in any case, you pass it a callback to run when the function is done. Typically, this callback could change a flag in your action node so that it can return a Running or a Success/Failure state...
- if you just need a behaviour to run over some period of time, you can always use the Update() and gradually increase a timer to approximate this.
But I'll admit those are just ideas, and again I haven't actually tried them out.
Hope it helps anyway,
cheers!
Thanks Mina!
I have created new abstract ActionNode: Node with some extra logic that handle async.
Come back to Unity ;)
@@minapecheux
Hey, happy you found a solution to your problem!@@JustFor-dq5wc
Haha sorry but for now, I don't plan on going back to Unity tutorials...!
Cheers :)
🙏
Hello there! I have a question about your code. In script "Node.cs", you declare access modifier of "constructor of Node class" by "public". But i think "Node" couldn't be exist itself. So i want to suggest to declare this access modifier by "protected". How about it? And... sorry for my bad English :(
Hello! You're totally right: we could/should turn the constructor into a "protected" one since we're not supposed to instantiate the Node class directly but rather use its child classes :)
I didn't dive into these "details" because I thought the tutorial would be a bit long if I also dived into inheritance and data encapsulation gotchas ^^
Nice remark, thanks for pointing this out!
Cheers :)
@@minapecheux Oh that was really quick reply! Thanks a lot of your tutorial video and reply. It really helped me to implement my Behaviour Tree. Even though i'm not good at English yet, your video was very simple and made me sence. From South Korea...
@@kimbab-nk9jc I'm really happy you liked the tutorial and found it useful for your project! And frankly, no worries: your English seems quite okay to me! Best of luck for your other programming ideas, cheers ;)
Very good tutorial thx👍
You're welcome, I'm really happy you like it! :)
@@minapecheux you should definitely make a Discord server for community feedback and requests👍
@@sk.mahdeemahbubsamy2857 That's definitely a good idea! I already have various platforms/socials to handle at the moment so it might be a bit difficult at the moment, but I will try and see if I can organize this someday! Thanks for the idea :)
Hi, do you have any advanced examples? Let's say enemy has 10 actions, how would I manage the complexity at that scale? What order do I write the actions in? I'm trying to find examples on bigger trees but it's really hard, any help would be appreciated! Thanks
Hello!
So:
1. the nice thing with behaviour trees is that you can actually build them gradually, by adding the nodes one at a time and expanding on the possible actions of your unit - so you could theoretically cut down the work and not have to do the 10 actions together :)
2. the order of the actions depends on the priority you want to give them ;)
Remember that given how we've implemented the Sequence/Selector control flow nodes, the actions on the left (i.e. the first children) are run first, then the second children are run, etc.
3. it's a bit hard to talk of "an abstract set of 10 actions", and I can't tell for sure where the complexity would reside in implementing your BT. But if you want a more involved example of a behaviour tree, I've actually discussed how to apply this exact design pattern in my series of RTS tutorials, more specifically in this sub-series of episodes:
mina-pecheux.medium.com/making-a-rts-game-23-implementing-behaviour-trees-for-our-units-1-3-unity-c-1a61840058a6
You'll see that I add more and more behaviour to my units, bit by bit.
I hope it helps,
feel free to comment again if you want more info!
Cheers :)
@@minapecheux One of the recent problems I had with behaviour trees is when an animation was only running in one node, I had an "attack" node which required "aiming" animation, but for the rest 9 actions i didnt want aiming. I ended up having this:
ATTACK
AIM = TRUE
INVESTIGATION
AIM = FALSE
WALK
AIM = FALSE
IDLE
AIM = FALSE
a lot of repeated code, any tips on not having that?
@@killereks I guess I would have some default value for AIM, set to FALSE, and so I only need to define it once in the "attack" node by setting it to TRUE in that specific case...
but again, it kinda depends on your setup, animator, etc ;)
@@minapecheux thank you so much!
This is really cool, but this has me interested in a non-recursive behavior tree.
Thanks, glad you liked the video! :)
Hmm, to be honest, I've never seen a non-recursive tree... from what I understand of this data structure, it's pretty inherent to a tree to be recursive, since it's all about stacking up nodes to form sub-branches ;)
But I could totally have missed something, so I hope you'll find a trick that suits your needs!
Cheers.
@minapecheux Well, guess its time to tread unknown grounds! My goal was wrapping several behavior trees underneath a Utility System/AI which decides what Behavior Tree I should flow through, dynamically changing behavior trees based on an AI's current situation.
I'm struggling with When to return Success or Running , Could you please give advice on that?
Hey! So it depends on the behaviour you want for your tree, and more precisely the way the flow nodes should work. Basically:
- a "running" state indicates that a node (and the children that are considered given the current situation) are currently computing something and haven't finished - for example, if your character moves from point A to point B, you can consider that while it's moving, the "move" node is in running mode
- a "success" state indicates that a node (and the children that are considered given the current situation) has finished its computing and managed to do everything has intended - for example, when your character reaches point B.
(the "failure" state is just the reverse: you stopped at one point during the computation because something went wrong... for example the terrain changed for some reason and your character cannot reach point B anymore!)
But again it's all quite specific to the specifics of your behaviour tree ;)
I hope it helps!
Cheers.
Hey Mina! Thanks so much for this tutorial, it's really helping me a lot. One question, if I add another soldier in the scene and they both go after the enemy with one soldier killing it before the other, I get a "MissingReferenceException: The object of type "Transform" has been destroyed on the other soldier. It's occurring during the CheckEnemyInAttackRange script. How can I check if the enemy is destroyed and go back to the root?
Hello - thanks for your nice comment, I'm really happy you like it! :)
Nice catch: indeed you would have to check that the Transform still exists. To do that, you can simply use a:
if (!t) { ... }
that will run if the referenced Transform has become invalid. I guess you'd also have to add this if-check in the CheckEnemyInFOVRange, just to be sure ;)
I hope it helps - feel free to tell me / send me some code via email (mina.pecheux@gmail.com) if it doesn't work or you have further questions!
Cheers :)
The patrol and target scripts work great but i do have a question. for the attack the ai does not stop to attack and just clips into the object with the enemy layer. is there anything that i could be missing to cause this?
Hello! Happy to know you're interested in the tutorial and the first features are working :)
Hmm, did you make sure to set the "attackRange" variable on the GuardBT script? Perhaps it's still with its default 0 value, and saw the CheckEnemyInAttackRange doesn't fire?
Feel free to post another comment if that doesn't work!
Cheers :)
@@minapecheux thanks for the response. after checking i upped it to both 5 and 10 for testing however the ai still just clips into the enemy without attacking. i should also add i do have the animator stuff commented out atm but unless that is the issue the script should be the same in the vid
@@caolanwalker4042 If you want, you can send me your Unity project (at mina.pecheux@gmail.com) and I'd be glad to try and help you out :)
(because it's not easy to do "remotely" like this, honestly I don't have any quick gotchas that pop to mind right now ^^)
@@minapecheux i have sent on a email with both the project file and a link to the github if u wish to clone it. i would like to say thank you so much for taking your time to help out with this
I have a question and am sort of new to this concept of behaviour trees but what is the purpose here of the nodes knowing what parents they have, in all other examples they only know the children and its there job to just update the child. All data shared seems to be accessed through a passed in behaviour tree which they all have access to. So is there a benefit foe this traversing up and down the tree mechanic you seem to use?
Hi!
It is absolutely a personal preference - we could also use a shared data storage that's completely decoupled from the tree. I find that storing the data directly in the tree allows me to better organize it and associate some branch-specific data to its corresponding nodes... but you can of course go for another (one-way) architecture, instead! :)
@@minapecheux Sorry if im not being clear so in your example you seem to do what i have seen in previous examples by having a blackboard variable in the tree class which they all query against but im unclear why you need to traverse up and down the tree. What is that used for?
@@charg1nmalaz0r51 Ah sorry - so if I understand properly your question... :)
So - the reason I go "up and down" is because I don't have one global blob of data.
I have one block of data per node (notice that my _dataContext variable is stored on a Node instance, not the entire Tree). It's empty by default but I can fill it with whatever I want since I'm using a Dictionary with object-typed values.
This data is then accessible to my node and all its children, since they can "crawl back" the chain of ancestors.
So, for example, I can define a variable in my root node, and it will be accessible to all nodes in the tree.
But the nice trick is that I can also define a variable also in a little "subtree" of my big behaviour tree, and only this part will know about it!
This helps a lot with encapsulating your data, and it allows you to "import/export" your subtrees to re-integrate behaviour from elsewhere. Re-importing would be harder with one big blob of data because you'd have to constantly update it when you bring in back a behaviour subtree from somewhere else - so you'd lose part of the "autonomy" of your subtree.
Does it help a bit? :)
Feel free to tell me if it's still not clear!
@@minapecheux gotcha so your seperate nodes hold their own data that it fills out the blackboard with if it needs to be shared otherwise its private to that node so if you want to make a new tree it doesnt have to be tightly coupled. For my purposes i probably wont reuse it so im going to make it slightly esier on myself with the tree knowing everything it needs to at all times.
@@minapecheux I have one more question if i may, how does a behaviour tree handle scenarios where your performing an action and its midway through the action and a higher node is selected because its conditions are met. Say for example the current node thats playing needs to finish or reset before switching over to the other node. Off top of my head maybe like an animation is told to play and it doesnt make sense to just jerk out of it instantly
how would you get over static fields? DI into the tree?
If you're referring to the static data variables like the speed, etc, I think there's a number of techniques you could use - DI could work, yup :)
I really tried to follow this the best I can but could not get it to work. The Guard gets stuck on going to 1 waypoint forever. It also keeps rotating around the waypoint.
Hi, and sorry to hear the tutorial is causing you problems!
Just to be sure: are you using the basic translation-based method shown here, or are you using a navigation mesh with a NavMeshAgent? (Cause having the unit "turn around the point" is typically a problem you can see if you don't have the right values for a nav agent ^^)
If you're using the technique shown here, then perhaps you need to adjust the distance check threshold (0.01 in the video) to better match the speed of your guard unit?
Hope it helps,
cheers! :)
I found it confusing for the get data and clear data func. You combined recursiveness and while loop and it seems creating double loop in the logic
The patrol works for me, but for some reason its not finding a target? it just stays in patrol. Any idea what I am missing?
I think the issue was, I am trying to implement this to a 2D game. So I can't use Physics.OverlapSphere
@@drewpoling6838 Hello! Indeed, if you're using 2D physics (for example 2D colliders), then you'll have to use the 2D methods as well: Physics2D.OverlapCircleAll, Physics2D.Raycast...
Hope it works for you, cheers! :)
@@minapecheux also for anyone else trying this in 2D - take the line _transform.LookAt(target.position); off in TaskGoToTarget. With this on your sprite will rotate on z and you wont be able to see it
@@drewpoling6838 Nice catch!
hi Mina for some reason my guard won't go to the enemy, do i need to create a tag for the enemy as target?
Hey! So here I'm not using a tag but a physics layer, actually, in order to optimise the search for enemies in the execution of the CheckEnemyInFOVRange node - if you take a look at 17:20, you'll see that we declare an "_enemyLayerMask" variable, and we set it to be the layer n°6 :)
So you'll need to go to the Inspector and set a new layer, using the Slot 6 (or another slot if it's already in use, but in that case remember to update the variable in the code!), and of course assign this layer to your enemy. Depending on your enemy prefab, you might have several nested objects - be sure to set (at least) the enemy layer for the object with the collider.
Hope it helps!
Cheers :)
will the fact that my game is in 2D affect it :-?
@@cioboatamihai-adrian1369 Hum, no ^^ in theory, the layers work the same
However!! If you're in 2D, you'll probably need to use the 2D-physic functions, and in particular here Physics2D.OverlapCircle() (docs.unity3d.com/ScriptReference/Physics2D.OverlapCircle.html)
@@minapecheux hey i managed to make it work instead of vector3.distance i used vector2.distance and it worked :D thank you so much for this amazing tutorial and for your time. really learned a lot today :D
@@cioboatamihai-adrian1369 Really happy you liked the video and found it useful - cheers! :)
Why does every node has it's own dataContext list? What is the advantage over just using one list in the root node?
It makes it easier to use any node as the root of its own subtree, and thus have a completely modular system :)
But you're right in that we could totally "mark" a node as root and only then, give it a data context!
Hope it clears things out,
cheers :)
is there a way to startCouroutine in the patrol script?? because I need it for my script but I cant
Hello!
So StartCoroutine can only be called from a MonoBehaviour, and since our TaskPatrol is a basic C# script, you can't use it here. But if you need some async behaviour to happen here, you can also rely on the C# tasks and the async/await C# syntax - if you're curious, I've talked about it and how it compares to coroutines in an article over here: medium.com/c-sharp-progarmming/running-async-code-in-unity-in-edit-mode-d9863cb2726f
(in the second half of the post)
Hope it helps,
cheers :)
Could somebody help me?
I did exactly the same as shown in the video except that my waypoints are only 3 and instead of a skeleton I got another unit that has another unedited Version of the "EnemyManager" script attatched to it.
It marks everything from every script where "Evaluate();" get's called as an error. I'm kinda confused :D
--> "Object refference not set to an instance of an object"
I know what this usually means but in this case....
Hello! I'm sorry you're having some issues with the tutorial!
About your error: do you have a bit more info about this null reference error? (In particular, if you click on it in the Unity console, you should be able to see which exact file+line it comes from > this will help you understand what causes this error ;) )
Out of the blue, the only things that come to mind in terms of references are: the waypoints themselves (that you have to assign in the GuardBT script), or perhaps a little mistake somewhere in the "SetupTree()" method that causes the children and parents to be improperly connected...
If you want, feel free to send me more details/your project by email, at mina.pecheux@gmail.com.
I'd be glad to take a closer look and try to solve this problem with you! :)
Cheers!