Skip to content

Commit

Permalink
Added guards, DynamicGuardSelector and subtree references
Browse files Browse the repository at this point in the history
  • Loading branch information
davebaol committed Dec 21, 2015
1 parent 7f4dc13 commit 071fda6
Show file tree
Hide file tree
Showing 12 changed files with 863 additions and 272 deletions.
6 changes: 5 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
[1.7.1]
- Updated to libgdx 1.7.2
- Now the behavior tree parser can report comments, which is useful for certain tools such as graphical editors.
- API Change and Addition: Behavior Tree API
* Added ability to guard any task.
* Added branch task DynamicGuardSelector.
* Now the text format supports subtree references which, besides improving reuse and readability, allows you to use guards as trees.
* Now the parser can report comments, which is useful for certain tools such as graphical editors.

[1.7.0]
- Updated to libgdx 1.7.1
Expand Down
55 changes: 53 additions & 2 deletions gdx-ai/src/com/badlogic/gdx/ai/btree/BehaviorTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class BehaviorTree<E> extends Task<E> {

private Task<E> rootTask;
private E object;
GuardEvaluator<E> guardEvalutor;

/** Creates a {@code BehaviorTree} with no root task and no blackboard object. Both the root task and the blackboard object must
* be set before running this behavior tree, see {@link #addChild(Task) addChild()} and {@link #setObject(Object) setObject()}
Expand Down Expand Up @@ -55,6 +56,7 @@ public BehaviorTree (Task<E> rootTask, E object) {
this.rootTask = rootTask;
this.object = object;
this.tree = this;
this.guardEvalutor = new GuardEvaluator<E>(this);
}

/** Returns the blackboard object of this behavior tree. */
Expand Down Expand Up @@ -111,11 +113,16 @@ public void childSuccess (Task<E> runningTask) {
/** This method should be called when game entity needs to make decisions: call this in game loop or after a fixed time slice if
* the game is real-time, or on entity's turn if the game is turn-based */
public void step () {
if (rootTask.status != Status.RUNNING) {
if (rootTask.status == Status.RUNNING) {
rootTask.run();
} else {
rootTask.setControl(this);
rootTask.start();
if (rootTask.checkGuard(this))
rootTask.run();
else
rootTask.fail();
}
rootTask.run();
}

@Override
Expand Down Expand Up @@ -163,6 +170,50 @@ public void notifyChildAdded (Task<E> task, int index) {
}
}

private static final class GuardEvaluator<E> extends Task<E> {

public GuardEvaluator (BehaviorTree<E> tree) {
this.tree = tree;
}

@Override
protected int addChildToTask (Task<E> child) {
return 0;
}

@Override
public int getChildCount () {
return 0;
}

@Override
public Task<E> getChild (int i) {
return null;
}

@Override
public void run () {
}

@Override
public void childSuccess (Task<E> task) {
}

@Override
public void childFail (Task<E> task) {
}

@Override
public void childRunning (Task<E> runningTask, Task<E> reporter) {
}

@Override
protected Task<E> copyTo (Task<E> task) {
return null;
}

}

/** The listener interface for receiving task events. The class that is interested in processing a task event implements this
* interface, and the object created with that class is registered with a behavior tree, using the
* {@link BehaviorTree#addListener(Listener)} method. When a task event occurs, the corresponding method is invoked.
Expand Down
9 changes: 7 additions & 2 deletions gdx-ai/src/com/badlogic/gdx/ai/btree/Decorator.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,16 @@ public Task<E> getChild (int i) {

@Override
public void run () {
if (child.status != Status.RUNNING) {
if (child.status == Status.RUNNING) {
child.run();
} else {
child.setControl(this);
child.start();
if (child.checkGuard(this))
child.run();
else
child.fail();
}
child.run();
}

@Override
Expand Down
9 changes: 7 additions & 2 deletions gdx-ai/src/com/badlogic/gdx/ai/btree/LoopDecorator.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,16 @@ public boolean condition () {
public void run () {
loop = true;
while (condition()) {
if (child.status != Status.RUNNING) {
if (child.status == Status.RUNNING) {
child.run();
} else {
child.setControl(this);
child.start();
if (child.checkGuard(this))
child.run();
else
child.fail();
}
child.run();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public void run () {
}
runningChild.setControl(this);
runningChild.start();
if (!runningChild.checkGuard(this))
runningChild.fail();
run();
} else {
// Should never happen; this case must be handled by subclasses in childXXX methods
Expand Down
27 changes: 26 additions & 1 deletion gdx-ai/src/com/badlogic/gdx/ai/btree/Task.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public enum Status {
/** The behavior tree this task belongs to. */
protected BehaviorTree<E> tree;

public Task<E> guard;

/** This method will add a child to the list of this task's children
*
* @param child the child task which will be added
Expand Down Expand Up @@ -125,6 +127,27 @@ public final void setControl (Task<E> control) {
this.tree = control.tree;
}

public boolean checkGuard(Task<E> control) {
if (guard != null) {
// Check the guard of the guard recursively
if (!guard.checkGuard(control)) return false;

// Use the tree's guard evaluator task to check the guard of this task
guard.setControl(control.tree.guardEvalutor);
guard.start();
guard.run();
switch (guard.getStatus()) {
case SUCCEEDED:
return true;
case FAILED:
return false;
default:
throw new RuntimeException();
}
}
return true;
}

/** This method will be called once before this task's first run. */
public void start () {
}
Expand Down Expand Up @@ -226,7 +249,9 @@ public Task<E> cloneTask () {
}
}
try {
return copyTo(ClassReflection.newInstance(this.getClass()));
Task<E> clone = copyTo(ClassReflection.newInstance(this.getClass()));
clone.guard = guard == null ? null : guard.cloneTask();
return clone;
} catch (ReflectionException e) {
throw new TaskCloneException(e);
}
Expand Down
116 changes: 116 additions & 0 deletions gdx-ai/src/com/badlogic/gdx/ai/btree/branch/DynamicGuardSelector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*******************************************************************************
* Copyright 2014 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/

package com.badlogic.gdx.ai.btree.branch;

import com.badlogic.gdx.ai.btree.BranchTask;
import com.badlogic.gdx.ai.btree.Task;
import com.badlogic.gdx.utils.Array;

/** A {@code DynamicGuardSelector} is a branch task that executes the first child whose guard is evaluated to {@code true}. At
* every AI cycle, the children's guards are re-evaluated, so if the guard of the running child is evaluated to {@code false}, it
* is cancelled, and the child with the highest priority starts running. The {@code DynamicGuardSelector} task finishes when no
* guard is evaluated to {@code true} (thus failing) or when its active child finishes (returning the active child's termination
* status).
*
* @param <E> type of the blackboard object that tasks use to read or modify game state
*
* @author davebaol */
public class DynamicGuardSelector<E> extends BranchTask<E> {

/** The child in the running status or {@code null} if no child is running. */
protected Task<E> runningChild;

/** Creates a {@code DynamicGuardSelector} branch with no children. */
public DynamicGuardSelector () {
super();
}

/** Creates a {@code DynamicGuardSelector} branch with the given children.
*
* @param tasks the children of this task */
public DynamicGuardSelector (Task<E>... tasks) {
super(new Array<Task<E>>(tasks));
}

/** Creates a {@code DynamicGuardSelector} branch with the given children.
*
* @param tasks the children of this task */
public DynamicGuardSelector (Array<Task<E>> tasks) {
super(tasks);
}

@Override
public void childRunning (Task<E> task, Task<E> reporter) {
runningChild = task;
running(); // Return a running status when a child says it's running
}

@Override
public void childSuccess (Task<E> task) {
this.runningChild = null;
success();
}

@Override
public void childFail (Task<E> task) {
this.runningChild = null;
fail();
}

@Override
public void run () {
// Check guards
Task<E> childToRun = null;
for (int i = 0, n = children.size; i < n; i++) {
Task<E> child = children.get(i);
if (child.checkGuard(this)) {
childToRun = child;
break;
}
}

if (runningChild != null && runningChild != childToRun) {
runningChild.cancel();
runningChild = null;
}
if (childToRun == null) {
fail();
} else {
if (runningChild == null) {
runningChild = childToRun;
runningChild.setControl(this);
runningChild.start();
}
runningChild.run();
}
}

@Override
public void reset () {
super.reset();
this.runningChild = null;
}

@Override
protected Task<E> copyTo (Task<E> task) {
DynamicGuardSelector<E> branch = (DynamicGuardSelector<E>)task;
branch.runningChild = null;

return super.copyTo(task);
}

}
10 changes: 7 additions & 3 deletions gdx-ai/src/com/badlogic/gdx/ai/btree/branch/Parallel.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,17 @@ public void run () {
lastResult = null;
for (currentChildIndex = 0; currentChildIndex < children.size; currentChildIndex++) {
Task<E> child = children.get(currentChildIndex);
if (child.getStatus() != Status.RUNNING) {
if (child.getStatus() == Status.RUNNING) {
child.run();
} else {
child.setControl(this);
child.start();
if (child.checkGuard(this))
child.run();
else
child.fail();
}

child.run();

if (lastResult != null) {
cancelRunningChildren(noRunningTasks ? currentChildIndex + 1 : 0);
if (lastResult)
Expand Down

0 comments on commit 071fda6

Please sign in to comment.