All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.badlogic.gdx.ai.btree.BehaviorTree Maven / Gradle / Ivy

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);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy