Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

behavior tree added #4

Merged
merged 1 commit into from
Oct 3, 2014
Merged

Conversation

implicit-invocation
Copy link

No description provided.

@davebaol
Copy link
Member

Wow! I'll review it soon. Thanks

@davebaol
Copy link
Member

Looks like a really interesting implementation. 👍
I was initially a bit surprised to see in the Node superclass methods like fail, success, running used to model node's status. I was more used to the return values FAIL, SUCCESS, and RUNNING. But then I realized that the general architecture can benefit from node's status implemented as methods. I like it.

However, there are some minor hiccups scattered across the code that make me think you are new to libgdx (nothing wrong with that, of course):

  • the use of java collections in place of libgdx collections, for example List and ArrayList instead of Array.
    Libgdx collections have some benefits, for example they support primitive types and minimize garbage collection activation. The latter is probably the most important aspect
    because libgdx is a cross-platform framework. Typically mobile platforms, which are the weakest ones, can benefit from libgdx collections. For example Android uses the Harmony
    Java implementation which really sucks.
  • the use of java reflection in place of libgdx reflection which supports the GWT backend too.
  • the use of java logging in place of libgdx logging

I can quickly make these changes myself if you want.

Finally I'd like to know @siondream 's opinion. :)
Of course the opinions of other people are welcome too.

@implicit-invocation
Copy link
Author

It would be nice if you make those changes.
I'm new to libgdx, just find this project interesting.

I think there are a lot of things we can do to improve this implementation (make it more cache frendly, add some other branch/decorator types, allow scripting and asynchronous...).
But the API is not likely to change (define Task and call tree.step() ).

@JesseTG
Copy link

JesseTG commented Sep 20, 2014

A Wiki page would be nice. Well, more than nice, actually. Essential.

@davebaol
Copy link
Member

@implicit-invocation
Before I can merge your PR we need you to sign the libgdx CLA.
Please add me to the mail recipients so I know when you've done: [email protected]

@davebaol
Copy link
Member

@implicit-invocation and everybody else interested in
For the first release I think this PR is ok, apart from minor issues mentioned above.

For the subsequent release I'd like to improve the text formalism supported by the parser.
Maybe a json-based formalism along these lines (it's a modified version of your dog.tree file):

[
  {task:selector, children: [
    {task:parallel, children: [
      {task:com.badlogic.gdx.ai.tests.btree.dogtasks.CareTask},
      {task:alwaysFail, children: [
        {task:com.badlogic.gdx.ai.tests.btree.dogtasks.RestTask}
      ]}
    ]},
    {task:sequence, children: [
      {task:com.badlogic.gdx.ai.tests.btree.dogtasks.BarkTask},
      {task:com.badlogic.gdx.ai.tests.btree.dogtasks.WalkTask},
      {task:com.badlogic.gdx.ai.tests.btree.dogtasks.BarkTask},
      {task:com.badlogic.gdx.ai.tests.btree.dogtasks.MarkTask}
    ]}
  ]}
]

Despite being more verbose it is much more powerful and expressive because it can easily support node-specific parameters. Also, libgdx already provides minimal-json capabilities.

Example: Limit decorator with count = 3

{task:limit, count:3, children: [
  {task:com.badlogic.gdx.ai.tests.btree.dogtasks.BarkTask}
]}        

Also I'd like to support a behavior tree library containing the archetypes for all the behavior trees loaded so far.
Then, if we add clone capability to the node superclass that will be overridden by actual nodes we can do some cool stuff like including sub-trees either at clone-time or at node-execution-time:

Pseudo-code:

public BehaviorTree createBehaviorTree(String archetypeName) {
    BehaviorTree archetype = behaviorTreeLibrary.get(archetypeName); // the 1st time the tree is loaded from file
    return archetype.cloneTree();
}

Example 1: Include task
The archetype behavior tree contains the "Include" node, but as soon as we instantiate (clone) our full tree
it replaces itself with a copy of the sub-tree, built by the library.
Notice that the sub-tree is instantiated when the behavior tree is created, ready for a character’s use.

{task:include, file:my_sub_tree}        

Example 2: IncludeOnDemand decorator
Initially it has no child but creates that child when it is first needed.
This can save memory when parts of a large behavior tree are rarely used. Think of a behavior tree
having a lot of branches for special cases: how to use a particular rare weapon, for example.
These highly specific sub-trees don't need to be created for every character, wasting memory; instead, they can be created on demand if the rare situation arises.

{task:includeOnDemand, file:my_sub_tree}

What do you think?

@davebaol
Copy link
Member

Hmmm... probably a XML format would be a better fit.
Also, libgdx provides an XMLReader that supports event-based parsing. Never used it though.

<root>
  <selector>
    <parallel>
      <com.badlogic.gdx.ai.tests.btree.dogtasks.CareTask/>
      <alwaysFail>
        <com.badlogic.gdx.ai.tests.btree.dogtasks.RestTask/>
      </alwaysFail>
    </parallel>
    <sequence>
      <com.badlogic.gdx.ai.tests.btree.dogtasks.BarkTask/>
      <com.badlogic.gdx.ai.tests.btree.dogtasks.WalkTask/>
      <com.badlogic.gdx.ai.tests.btree.dogtasks.BarkTask/>
      <com.badlogic.gdx.ai.tests.btree.dogtasks.MarkTask/>
    </sequence>
  </selector>
</root>
<limit count="3">
  <com.badlogic.gdx.ai.tests.btree.dogtasks.RestTask/>
</limit>
<include file="my_sub_tree"/>        
<includeOnDemand file="my_sub_tree"/>        

@implicit-invocation
Copy link
Author

I've just sent the CLA, my printer is out of ink since forever.

About your idea, I totally agree with the parameter part, the clone part, the include part and the lazy part.
For paramters, we can easily allow some simple data types: number, string, boolean, even subtree reference (register and lookup by ID).
I'm thinking of a key-value store, parser will call method set(key, value) of nodes, and nodes can refer to their own paramters by get(key). That approach is similar to setter injection in Spring.

About the definition file format, I prefer JSON over XML, and a conventional indent-based over JSON.
Opening and closing those XML tags, adding all those JSON double-quotes, it's not something you want to do everyday.
We can make the text format more formal.

type:sequence #we can use type for known/registered types
    task:fromClass className:"packageName.className" #only string must be quoted
    task:fromScript scriptFile:"script_file_path"
    type:limit count:3 #number
        type:some_registered_task
    type:include file:"path_to_file" lazy:true #boolean

Just my 2 cent though.

davebaol added a commit that referenced this pull request Oct 3, 2014
@davebaol davebaol merged commit 56a3474 into libgdx:master Oct 3, 2014
@davebaol
Copy link
Member

davebaol commented Oct 3, 2014

Merged, thanks :)

@davebaol
Copy link
Member

davebaol commented Oct 4, 2014

@implicit-invocation
I'd like to discuss some improvements with you. :)
How can I contact you?
You can find me on the IRC channel #libgdx on irc.freenode.net

@implicit-invocation
Copy link
Author

I sent you an email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants