com.badlogic.gdx.ai.btree.BehaviorTree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gdx-ai Show documentation
Show all versions of gdx-ai Show documentation
AI algorithms for libGDX
The newest version!
/*******************************************************************************
* 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;
import com.badlogic.gdx.utils.Array;
/** The behavior tree itself.
*
* @param type of the blackboard object that tasks use to read or modify game state
*
* @author implicit-invocation
* @author davebaol */
public class BehaviorTree extends Task {
private Task rootTask;
private E object;
GuardEvaluator guardEvaluator;
public Array> listeners;
/** 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()}
* respectively. */
public BehaviorTree () {
this(null, null);
}
/** Creates a behavior tree with a 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()}
* respectively.
*
* @param rootTask the root task of this tree. It can be {@code null}. */
public BehaviorTree (Task rootTask) {
this(rootTask, null);
}
/** Creates a behavior tree with a root task and a 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()}
* respectively.
*
* @param rootTask the root task of this tree. It can be {@code null}.
* @param object the blackboard. It can be {@code null}. */
public BehaviorTree (Task rootTask, E object) {
this.rootTask = rootTask;
this.object = object;
this.tree = this;
this.guardEvaluator = new GuardEvaluator(this);
}
/** Returns the blackboard object of this behavior tree. */
@Override
public E getObject () {
return object;
}
/** Sets the blackboard object of this behavior tree.
*
* @param object the new blackboard */
public void setObject (E object) {
this.object = object;
}
/** This method will add a child, namely the root, to this behavior tree.
*
* @param child the root task to add
* @return the index where the root task has been added (always 0).
* @throws IllegalStateException if the root task is already set. */
@Override
protected int addChildToTask (Task child) {
if (this.rootTask != null) throw new IllegalStateException("A behavior tree cannot have more than one root task");
this.rootTask = child;
return 0;
}
@Override
public int getChildCount () {
return rootTask == null ? 0 : 1;
}
@Override
public Task getChild (int i) {
if (i == 0 && rootTask != null) return rootTask;
throw new IndexOutOfBoundsException("index can't be >= size: " + i + " >= " + getChildCount());
}
@Override
public void childRunning (Task runningTask, Task reporter) {
running();
}
@Override
public void childFail (Task runningTask) {
fail();
}
@Override
public void childSuccess (Task runningTask) {
success();
}
/** 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) {
rootTask.run();
} else {
rootTask.setControl(this);
rootTask.start();
if (rootTask.checkGuard(this))
rootTask.run();
else
rootTask.fail();
}
}
@Override
public void run () {
}
@Override
public void resetTask () {
super.resetTask();
tree = this;
}
@Override
protected Task copyTo (Task task) {
BehaviorTree tree = (BehaviorTree)task;
tree.rootTask = rootTask.cloneTask();
return task;
}
public void addListener (Listener listener) {
if (listeners == null) listeners = new Array>();
listeners.add(listener);
}
public void removeListener (Listener listener) {
if (listeners != null) listeners.removeValue(listener, true);
}
public void removeListeners () {
if (listeners != null) listeners.clear();
}
public void notifyStatusUpdated (Task task, Status previousStatus) {
for (Listener listener : listeners) {
listener.statusUpdated(task, previousStatus);
}
}
public void notifyChildAdded (Task task, int index) {
for (Listener listener : listeners) {
listener.childAdded(task, index);
}
}
@Override
public void reset() {
removeListeners();
this.rootTask = null;
this.object = null;
this.listeners = null;
super.reset();
}
private static final class GuardEvaluator extends Task {
// No argument constructor useful for Kryo serialization
@SuppressWarnings("unused")
public GuardEvaluator () {
}
public GuardEvaluator (BehaviorTree tree) {
this.tree = tree;
}
@Override
protected int addChildToTask (Task child) {
return 0;
}
@Override
public int getChildCount () {
return 0;
}
@Override
public Task getChild (int i) {
return null;
}
@Override
public void run () {
}
@Override
public void childSuccess (Task task) {
}
@Override
public void childFail (Task task) {
}
@Override
public void childRunning (Task runningTask, Task reporter) {
}
@Override
protected Task copyTo (Task 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.
*
* @param type of the blackboard object that tasks use to read or modify game state
*
* @author davebaol */
public interface Listener {
/** This method is invoked when the task status is set. This does not necessarily mean that the status has changed.
* @param task the task whose status has been set
* @param previousStatus the task's status before the update */
public void statusUpdated (Task task, Status previousStatus);
/** This method is invoked when a child task is added to the children of a parent task.
* @param task the parent task of the newly added child
* @param index the index where the child has been added */
public void childAdded (Task task, int index);
}
}