Coder Social home page Coder Social logo

kagenash1 / godot-behavior-tree Goto Github PK

View Code? Open in Web Editor NEW
190.0 11.0 24.0 266 KB

A GDScript implementation of a behavior tree for AI, built through Godot nodes.

License: Other

GDScript 100.00%
godot ai artificial-intelligence game-development behavior-tree

godot-behavior-tree's Introduction

godot-behavior-tree

A GDScript implementation of a behavior tree for game AI, based on native Godot nodes and using the built in scene tree editor.

C# VERSION -> https://github.com/MadFlyFish/godot-behavior-tree-csharp

INSTALLATION

  • Copy the 'addons' folder into the main directory of your project.
  • Enable the plugin from project settings, THEN RESTART Godot, otherwise it will not recognize the new classes (that's a bug of the engine).
  • Optionally, you can also drag the bt_example folder into the main directory of your project.
  • To see the example working, run the agent.tscn scene. The ex_behavior_tree.tscn scene is an example of how the tree is built.

alt textalt text

INSTRUCTIONS:

  • Click the node creation icon. You should see new nodes available (if you don't, restart Godot). You must use a BehaviourTree as the root node, which should have only a single child. This child can be any of the nodes under the BTNode category, which all inherit from the BTNode class.
  • After creating a behavior tree, you must specify who is the owner of the AI (the Agent) and what Blackboard is being used. Blackboards can be put anywhere (given it is outside the tree), and even shared among different trees. The system is flexible enough to allow you to decide how and when to update your blackboard data. For example, you can make a plain Node with a script that handles updating the blackboard, eg. with signal callbacks or even in process(). Or you can do it from anywhere else, even from inside the tree, just make sure to design things in a way you can maintain and keep track of.
  • A Behavior Tree flows executing each of its children, which return some kind of success or failure state. Only those branches following a successful node will be executed. A BTNode must return either success or failure, and can suspend execution only with a yield() call, after which it will remain in a running state until execution is completed. When a BTNode is in running state, the tree will progressively suspend execution (with the only exeption being BTParallel ) until all of the children complete execution. This is for optimisation purposes.
  • The flow of the tree is defined by the so called composite nodes: BTSequence, BTSelector, BTRandomSelector, BTRandomSequence, BTParallel which all inherit from BTComposite. A sequence is successfull if all the children are successful, while it fails if one of the children fails. The selector is the logical opposite, it succeeds if one children succeeds, and fails if all the children fail. A parallel will run all the children and always succeed regardless, WITHOUT waiting for children to complete execution. The base composite node runs all the children and always succeeds, but it also waits for execution completion.
  • The actions of your AI behavior are carried out in BTLeaf nodes. Add a BTLeaf, then do 'extend script'. Now you can define your own behavior in this script by overriding the _ tick() method. Your actions will go here. Make sure to read the comments in the base script to know the best practices. Also remember BTLeaf shouldn't have children.
  • BTDecorator is used to customise the execution of a child node. They can only have ONE child.
  • BTConditional is the most common type of decorator. Add a BTConditional and extend the script, then override the _ pre_tick() method to define the conditions under which the child will be executed. Make sure you read the comment cause there is a useful example there.
  • BTGuards are decorators which can be used to temporarily lock branches. Optionally, you can assign an unlocker, which will override the lock time specified. There is also the option to assign a locker. BTGuards can make your behavior very rich and reactive, as well as optimised, as they avoid unnecessary branching and repetition.
  • Other decorators allow you to loop execution, reverse the result of tick, and so on. There is a lot you can do by customising execution through decorators.
  • Good practice is to use the provided nodes and follow the design pattern of the behavior tree. But since this is a purely code based implementation without any visual editor, you have a lot of control over the design and thus a margin of error. These are just useful scripts that follow some "good practices" but are not bound to them, if not for a couple of basic rules. It is up to you to decide how you design your behavior tree, but keep in mind that if you misuse it you will not benefit from the power of the behavior tree pattern. (for example, you could even use the base BTNode for everything and just extend it everytime, although it would be a mess)
  • You could have a huge behaviour tree, but the best practice is to follow the component philosophy of Godot and make several smaller behaviour trees for each component of your scene. For example, a tree for your movement controller, a tree for your weapon controller, a tree for your pathfinder component, etc.. A behaviour tree can only have one blackboard, but the same blackboard can be used by many trees, so this is particularly handy if you wanna have several trees without also making multiple blackboards. Personally, the reason why I have the blackboard as a decoupled component, is because I wanted to make squads of enemies sharing the same data but behaving independently, so this is a use case for this. Moreover, I usually have several components in my actors, and I wanna use the same database for different tree.

godot-behavior-tree's People

Contributors

kagenash1 avatar shargon-pendragon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

godot-behavior-tree's Issues

Add repeat frequency to bt_repeat and bt_repeat_until

The repeater nodes currently wait for running actions, but do not wait for the next frame. This can easily result in unwanted behavior, for example when repeating move_and_slide(), as it will execute it in a loop and will result in the agent suddenly snapping.

Can easily be fixed by waiting for a given time at the end of the loop.

Make better looking icons

I literally made the icons on paint. Is there a generous artist willing to make some better looking and more artistically consistent icons?
They should be small (eg: 18x18) with a bit of a margin (eg: 2 pixels of marging and 16x16 icon)
The format should be .svg

C++ Custom Native Module

I'm amazed by this plugin, do you have interest in creating a secondary branch with a native custom module implementation?

It requires re-compilation of the editor to be used, but would add nice features like the classes documentation in Search Help and better runtime performance.

Function state of running BTNodes resumes on destroyed instances.

A lot of BTNodes and BBServices suspend execution through yield() calls. This is good as it avoids running things at each frame and allows more control over the flow of the script. However, if we destroy an object, a lot of errors will be thrown because those function states are resumed after a script/class instance has been destroyed (as in the example below).

Screenshot 2021-03-09 005449

Interrupting flow

This may already be covered, but the current implementation doesn't provide any obvious solution on how to do this. If you an AI agent perform a sequence using things like wait timers or other yields, there doesn't seem to be any way to get them to re-evaluate from a higher level node based on external stimuli. An example might be an AI who simply wanders around, but if they get attacked, you would want their wander sequence interrupted and may instead go into an attack sequence.

The "abort_tree " signal may have the wrong target object

As you can see in the screenshot bellow, what you want to abort may be the "BehaviorTree" node, but actually in my case it can raise an error, because the real "owner" is "ai_test" node and it doesn't have any "abort( )" function. :)

image
image

Question about Few Things and Thank you for supporting this! <3

So, I have been looking into different behavior trees and everything to jumpstart. This is probably the best I have seen so far and loving the constant support. I tried few behaviors with timer and it's pretty fun to see the flow right on the tree.
I will start with the questions:

  • Why did you opt for physics_frame and idle_frame approach than normal process and physics_process? Not going to lie, I am loving this and did learn a new thing. Are they even different from godot's own built in functions (idle = process, physics = physics_process)?

  • I created this custom patrol behaviour (it's barebones right now) but I keep getting assertion failed because of "not running()". The code is below.

go_to_position.gd

extends BTLeaf

func _tick(agent: Node, blackboard: Blackboard) -> bool:
	var ag = agent as Agent
	ag.move_towards_position(Vector2(85, 190), blackboard.get_data('delta'))
	if ag.global_position.distance_to(Vector2(85, 190)) <= 50:
		return succeed()
	return running()

agent.gd

extends KinematicBody2D
class_name Agent

export (NodePath) var blackboard_path
export (float) var speed

onready var blackboard = get_node_or_null(blackboard_path)
onready var sprite: Sprite = $Sprite

var velocity: Vector2

func _ready():
	blackboard.set_data('position', $Node/Position2D.global_position)

func _physics_process(delta):
	blackboard.set_data('delta', delta)

func move_towards_position(target_position: Vector2, delta: float):
	print(target_position)
	var difference = (target_position - position).normalized()
	velocity = move_and_slide(difference * speed, Vector2.UP)
	velocity.normalized()

I am pretty sure it's my mistake, if so.. I would love to know fix and few pointers based on the script. You have been massive help to my workflow, I would love to continue using this.

Visual editor

Thanks for this great addon!

  • I have being using it for a couple of weeks, and still looking forward to more future updates. Recently I am developing a tool, which uses Xmind and excel as an tree editor and create behavior tree dynamically in run-time, mostly based on your addon‘s code.

  • That helps my work a lot. Therefore I am introducing your addons to my Godot developer friends in China, and they show great interest too.

  • But still, an external editor is not so handy as desired. I suggest developing a behavior tree editor, which makes use of Godot's internal editor . That may make AI editing and debugging a lot easier.

  • I am look forward to some progress on this. LOL

Here are some screenshots of my external editor. I am grad to share this tool if someone needs.

image

image

image

image

image

image

Originally posted by @MadFlyFish in #8

Some Suggestions

Ok, I've found some things that could be changed to improve optimization max as possible:

Behavior_tree.gd

  • The debug option,( seems to make no difference here?):
    image

  • Remove the Red box, and add the green one:

image

Other

I looked at other scripts, there's nothing more I can find to improve

About a Manager

What you can do is a manager script (tool) that check for the "BehaviorTree" node in the current edited scene, and check at every tree_changed event if the childs are correct (number of childs, expected child node, and so on)

Debug Stuff

I think that, instead of checking if (debug) everytime, you can make debug functions to be called instead of the main ones e.g:
run() and run_debug(). Or new classes like Selector and SelectorDebug, there are lots of ways.

Final Toughts

  • My english is not one of the bests, but I think I've passed the essence of things here
  • This ideas are not mandatory, but just some toughts that can evolve...

ISSUSE occurs when using BTGuard Node!

I am new to the behavior tree,still learning .
today I create a test project and try build one simple behavior tree. looks like thi
捕获
s:
I use a parallel node as root, and first child of root is response to scan the target, measure the distance between. and CheckFound is also the lock and unlock object of BTGuard node (true->unlock,false->lock)
second child of root is the BTGuard node,which I want to be unlock when target is found.

when I run the test ,this error appears:
"Invalid BTNodeState assignment. Can only set to success or failure."

the debug result shows that when executing the tick() method of the Selector Node, its already running, so in the _tick() method of
BTGuard ,it get a result of false ,and child state is still running.

maybe I've misuse the BTGuard node because Im new to behavior tree ,but still want to know when and how to properly use BTGuard and do not run into problems!

Improve BBService structure to allow user control on the run() loop

Services are supposed to run at the specified frequency. So the run() function calls the virtual _run() at each loop iteration and handles the execution frequency so that the user only needs to worry about the custom logic.

However, if the user wants to preallocate memory outside of the loop (like storing immutable data in a variable to avoid repeatedly creating and destroying references for something that doesn't ever change), this is not possible as the virtual function _run() is called at each loop iteration.

Improvements to the structure are needed to allow memory optimization while maintaining the desired execution flow of the service, with the run() loop iterating at the specified frequency and the user only overriding the virtual _run().

Screenshot 2021-03-09 010621

Seems to ignore tree.paused

Hi, I have been using your plugin with great success so far, so thanks for your work!

However I have now implemented a pause menu of sorts and the behavior tree seems to be still executing - basically even though everything else is frozen, my agents keep moving forward but do nothing else.

I don't know if you're still working on this but if you do this might be something to look into.

Edit: I did modify the script a little and that seems to do it. Basically only tick the child if the tree is unpaused.

func run():

	var blackboard: Blackboard = get_node(_blackboard)
	var bt_root: BTNode = get_child(0)
	var agent: Node = get_node(_agent)
	var tick_result
	
	while is_active:
		if !get_tree().paused:		
			tick_result = bt_root.tick(agent, blackboard)
		
			if tick_result is GDScriptFunctionState:
				tick_result = yield(tick_result, "completed")
		
		if sync_mode == "idle":
			yield(get_tree(), "idle_frame") 
		else:
			yield(get_tree(), "physics_frame")

Plugin Root Path Doesn't Exist

I'd suggest using the standard setup of "addons/<plugin_name>" - IF this is intended to be an asset rather than a demo or proof of concept.

Also, the required directory root doesn't exist (see attached pic) and won't ever exist. (see note about actual dir names below)

godot_btree_bug

Actual dir names by download method:

  1. github: godot-behaviour-tree-master
  2. Godot Asset Library: godot-behavior-tree-3d8e1fc3d95ce7e5d3adafda9546fdf6f47a0a38 (dynamic, will continue to change)

Small issue(s), that can be easily fixed by the end user, but:

  1. Why make the install harder than necessary?
  2. If I see problems with the initial setup and bare minimum functionality, I lose trust in the whole project. I'm sure there are others who are the same.

Godot 4.0 branch

Hey there,

I started porting this plugin to Godot 4.0 on a branch in my own fork here. Some of the basic nodes like sequence and leaf nodes seem to work as expected. There are almost certainly bugs due to the rewrite of gdscript.

To save anyone interested from digging into the commits the current concerns in rough order of importance

  1. There is no more GDScriptFunctionState
  2. State variable of BTNode is not longer setget. Leaving it as a setget makes any behavior tree crash at runtime.
  3. Cannot have a signal and function named tick in BTNode.
  4. Ongoing bug with onready with new gdscript breaks some decorator nodes. They have been removed from the plugin for now.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.