Git Product home page Git Product logo

Comments (11)

MadFlyFish avatar MadFlyFish commented on June 7, 2024 4

Thanks for your great addons! 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 addons.
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.
May I have your email or other contacts and learn some more about AI? Thanks.

image
image
image
image
image
image

from godot-behavior-tree.

kagenash1 avatar kagenash1 commented on June 7, 2024

I see what you mean.
If you want to make a branch conditional, you put it under a decorator, extend the decorator and define your condition there (as it's done in the example), but if your sequence (or any other node) is in a running state it can't be interrupted until completion, at least not in a straightforward way.

I will definitely work on making this possible, maybe with an event driven approach as it's done in Unreal.
Thank you for bringing it up, this is a work in progress and I'm trying to improve it everyday!

Allowing abortion will require some design changes so it may take a bit longer than other smaller improvements I have planned.
The best thing you could do until I implement this, is to put your 'states' (wandering, attacking, etc.) under a Parallel node and enclose them in a decorator. A parallel node allows you to execute all the children regardless of result and without waiting for execution.
Something like this:
Screenshot 2021-03-15 082938

Obviously there are a few extra steps to this solution: because it won't abort the previous sequence, you may have overlapping behavior. So it depends on how things are on your end, but if you are just waiting you can put another condition after the wait is completed to check if you are still in, say, 'wandering state' and return fail() if you are not, so the sequence doesn't continue.

Another thing is putting a while loop inside your decorator and keep ticking as long as 'wandering' is true.

So ultimately, you have a lot of control over how you design it: my suggestion is to prefer short, repeated actions rather than tasks that take very long to complete. This is the way I see a behavior tree should be designed, but what you suggested is definitely something that other implementations have and that should be possible for the user to do.

Also keep in mind that using the Behavior Tree like this makes it kinda like a state machine. So if you wanna design things in that way, a state machine is probably more suitable (I recommend https://gitlab.com/atnb/xsm)

from godot-behavior-tree.

SaracenOne avatar SaracenOne commented on June 7, 2024

Yeah, trying parallel nodes, I was able to develop some AI which addressed my problem reasonably well. I think taking your advice of thinking about behaviour trees less in the terms of finte state machines helps too. I am fairly new to them. That said, I still think there's value in the Unreal-style approach of branch aborting, especially as trees start to balloon in complexity.

from godot-behavior-tree.

kagenash1 avatar kagenash1 commented on June 7, 2024

Good :) Parallels are the "worst" in terms of performance so, if you can, think of other ways.
Always happy to provide support if you need! I know it's not very user friendly yet but I'm actively trying to improve it and make it as complete and powerful as possible.
I'm onto the flow abortion now so it will be a thing in the next update. Also, the way running states are handled will change with it, so I'm gonna have to make sure the update doesn't break your behavior trees.

from godot-behavior-tree.

kagenash1 avatar kagenash1 commented on June 7, 2024

It's amazing to see this! I'm glad my addon turned out useful and I'm excited to see what you've done with it.
My email is [email protected] or if you want you can find me on Discord at kagenashi#8224

(By the way, this is not really an issue nor it is related to the issue above, so next time open a new discussion instead)

from godot-behavior-tree.

kagenash1 avatar kagenash1 commented on June 7, 2024

This issue will be addressed in the 1.2.rc4 version.

from godot-behavior-tree.

SaracenOne avatar SaracenOne commented on June 7, 2024

Nice!

from godot-behavior-tree.

kagenash1 avatar kagenash1 commented on June 7, 2024

I have started updating the 1.2.rc4 branch. There are some changes already, although the features is not there yet. However, I made a much more complex example to show how to achieve some of the behaviors you people were talking about!

That shows how the tree can be made responsive and flexible. I think that even without a "interrupt flow" function this is getting the job done nicely and encapsulates very complex logic with just a few nodes.

from godot-behavior-tree.

mcccclean avatar mcccclean commented on June 7, 2024

Hello, weighing in on this quite late - just here to say that I've had some success implementing this locally without too much refactoring, and I wanted to share my approach in case it is useful toward the official implementation (or to others finding this thread).

In bt_node.gd (unrelated sections elided):

signal completed(result)
const ABORTED = -1;

func abort():
  fail()
  complete(ABORTED)

func complete(value):
  if running():
    return
  emit_signal("completed", value)

func tick(agent: Node, blackboard: Blackboard) -> bool:
  # .... etc ...
  if result is GDScriptFunctionState:
    assert(running(), "BTNode execution was suspended but it's not running. Did you succeed() or fail() before yield?")
    result.connect("completed", self, "complete")
    result = yield(self, "completed")
    if result is int and result == ABORTED:
	    return

and then in BehaviourTree:

# halt the behaviour tree
func interrupt() -> void:
  bt_root.propagate_call("abort")

# halt the behaviour tree and restart it
# (useful for scenarios where an agent is surprised, eg taking damage, enemy arrives in range etc)
func hard_restart() -> void:
  interrupt()
  is_active = true
  start()

The central change there is that the yield inside tick can now be terminated early by calling complete on the node, rather than always having to wait for the underlying function call to finish. This allows for mechanisms that short circuit the node into a success or failure state as well as aborting it.

Of course there are some downsides to this approach - the function that the node was waiting on will still continue its execution uninterrupted, which might have strange side effects. This won't come up if nodes are designed to only do one thing (like, a node whose job is to wait and then execute a side effect should probably be split up into a waiter and an executor node) but perhaps this is too big of a tripping hazard to include without further safeguards. I also don't know how performant it is; my project only includes a handful of actors so it might have additional problems when dozens/hundreds of agents are in the scene.

Thanks @GabrieleTorini for the great module - it's so useful and extensible, it's been a great help to my hobby projects. I hope this is useful to you!

from godot-behavior-tree.

kagenash1 avatar kagenash1 commented on June 7, 2024

Thanks! This is definitely a great addition.
Would you like to make a PR for this? I'll try to see if it works with the sample project.
(I'm not a geet (git geek) so the whole process of the PR is probably gonna be clunky)

I'm glad you're happy with the plugin. It was a big experiment, and thinking back I think it could be done in an entirely different way, but as long as it's stable and gets you to make your AI, I'm satisfied!

from godot-behavior-tree.

ATHellboy avatar ATHellboy commented on June 7, 2024

Hello, weighing in on this quite late - just here to say that I've had some success implementing this locally without too much refactoring, and I wanted to share my approach in case it is useful toward the official implementation (or to others finding this thread).

In bt_node.gd (unrelated sections elided):

signal completed(result)
const ABORTED = -1;

func abort():
  fail()
  complete(ABORTED)

func complete(value):
  if running():
    return
  emit_signal("completed", value)

func tick(agent: Node, blackboard: Blackboard) -> bool:
  # .... etc ...
  if result is GDScriptFunctionState:
    assert(running(), "BTNode execution was suspended but it's not running. Did you succeed() or fail() before yield?")
    result.connect("completed", self, "complete")
    result = yield(self, "completed")
    if result is int and result == ABORTED:
	    return

and then in BehaviourTree:

# halt the behaviour tree
func interrupt() -> void:
  bt_root.propagate_call("abort")

# halt the behaviour tree and restart it
# (useful for scenarios where an agent is surprised, eg taking damage, enemy arrives in range etc)
func hard_restart() -> void:
  interrupt()
  is_active = true
  start()

The central change there is that the yield inside tick can now be terminated early by calling complete on the node, rather than always having to wait for the underlying function call to finish. This allows for mechanisms that short circuit the node into a success or failure state as well as aborting it.

Of course there are some downsides to this approach - the function that the node was waiting on will still continue its execution uninterrupted, which might have strange side effects. This won't come up if nodes are designed to only do one thing (like, a node whose job is to wait and then execute a side effect should probably be split up into a waiter and an executor node) but perhaps this is too big of a tripping hazard to include without further safeguards. I also don't know how performant it is; my project only includes a handful of actors so it might have additional problems when dozens/hundreds of agents are in the scene.

Thanks @GabrieleTorini for the great module - it's so useful and extensible, it's been a great help to my hobby projects. I hope this is useful to you!

Hey there,

I'm using this piece of code for hard restarting the bt but it doesn't work for me. At least it doesn't work when the current leaf is under RepeatUntil. Have you checked it with RepeatUntil too ?

from godot-behavior-tree.

Related Issues (18)

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.