Skip to content

Instantly share code, notes, and snippets.

@HaddadBenjamin
Last active February 6, 2016 09:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HaddadBenjamin/5de6acdf4c19a1335838 to your computer and use it in GitHub Desktop.
Save HaddadBenjamin/5de6acdf4c19a1335838 to your computer and use it in GitHub Desktop.
Behaviour Tree Custom
/// <summary>
/// Classe d'abstraction regroupant les noeuds permettant de parcourir l'arbre, c'est à dire une sequence ou un selector.
/// </summary>
public abstract class ABehaviourNodeTraversal : ABehaviourNode
{
#region Fields
private ABehaviourNodeDecorator decorator;
#endregion
#region Attributes
public ABehaviourNodeDecorator Decorator
{
get { return decorator; }
set { decorator = value; }
}
#endregion
#region Constructor
public ABehaviourNodeTraversal()
{
this.decorator = new BehaviourNodeDecoratorDefault();
}
#endregion
}
/// <summary>
/// Contient la condition permettant de déterminer si on peut lancer une tâche ou non.
/// </summary>
public abstract class ABehaviourNodeDecorator
{
#region Fields
private ABlackboard blackboard;
#endregion
#region Attributes
public ABlackboard Blackboard
{
get { return blackboard; }
set { blackboard = value; }
}
#endregion
#region Abstract Methods
/// <summary>
/// Détermine si on peut ou non lancer la tâche.
/// </summary>
public abstract bool CanPerformTask();
#endregion
}
/// <summary>
/// Contient une action qui sera lancé si la condition du decorator est bonne.
/// </summary>
public abstract class ABehaviourNodeTask : ABehaviourNode
{
#region Constructor
public ABehaviourNodeTask()
{
this.Type = EBehaviourNodeType.Task;
}
#endregion
}
/// <summary>
/// On ne peut qu'hériter de cette classe pour pouvoir redéfinir notre classe de donnée.
/// </summary>
public abstract class ABlackboard
{
#region Abstract Methods
/// <summary>
/// Permet de mettre à jour les valeurs du blackboard.
/// </summary>
public abstract void Update();
#endregion
}
/// <summary>
/// Classe dont chaque noeud hérite, cette dernière est composé d'un tableau de noeuds et du blackboard.
/// </summary>
public abstract class ABehaviourNode
{
#region Fields
private EBehaviourNodeType type;
private ABehaviourNode[] nodes;
//Les nodes traversal ne devrait en avoir besoin, cependant ceci obligerai de reset tous les blackboard de chaque noeud.
private ABlackboard blackboard;
#endregion
#region Properties
protected EBehaviourNodeType Type
{
get { return type; }
set { type = value; }
}
public ABehaviourNode[] Nodes
{
protected get { return nodes; }
set
{
nodes = value;
Blackboard = blackboard;
}
}
public ABlackboard Blackboard
{
protected get { return blackboard; }
set
{
blackboard = value;
if (null != nodes)
{
for (byte i = 0; i < nodes.Length; i++)
{
nodes[i].Blackboard = blackboard;
if (nodes[i].IsOfType(EBehaviourNodeType.Selector) || nodes[i].IsOfType(EBehaviourNodeType.Sequence))
{
ABehaviourNodeTraversal nodeTraversal = nodes[i] as ABehaviourNodeTraversal;
if (null != nodeTraversal)
{
ABehaviourNodeDecorator nodeDecorator = nodeTraversal.Decorator;
if (null != nodeDecorator)
nodeDecorator.Blackboard = blackboard;
}
}
}
}
}
}
#endregion
#region Constructor
public ABehaviourNode()
{
this.type = EBehaviourNodeType.NotDefine;
}
#endregion
#region Abstract Methods
//Execute : pour les taches, PerformTask pour les taches ?
public abstract EBehaviourExecuteType Execute();
#endregion
#region Behaviour Methods
public bool IsOfType(EBehaviourNodeType type)
{
return this.type == type;
}
//Ne devrait pas être ici mais c'est plus pratique.
public bool CanRetrieveADecoratorAndPerformHisTask()
{
ABehaviourNodeTraversal nodeTraversal = this as ABehaviourNodeTraversal;
if (null != nodeTraversal)
{
ABehaviourNodeDecorator nodeDecorator = nodeTraversal.Decorator;
if (null != nodeDecorator)
{
if (nodeDecorator.CanPerformTask())
return true;
}
}
return false;
}
#endregion
}
/// <summary>
/// Noeud permettant de parcourir l'arbre de sorte à s'arrêter automatiquement si un des noeuds enfant peut lancer son action.
/// </summary>
public class BehaviourNodeSelector : ABehaviourNodeTraversal
{
#region Constructor
public BehaviourNodeSelector()
{
this.Type = EBehaviourNodeType.Selector;
}
#endregion
#region Override Methods
/// <summary>
/// Find the first node where the decorator can perform the task.
/// </summary>
public override EBehaviourExecuteType Execute()
{
EBehaviourExecuteType executeType = EBehaviourExecuteType.Fail;
if (null != this.Nodes)
{
foreach (ABehaviourNode node in this.Nodes)
{
//je récupére le décorateur et si il m'autorise à lancer les sous neouds, je les lance puis je stop l'arbre.
if (node.IsOfType(EBehaviourNodeType.Selector) || node.IsOfType(EBehaviourNodeType.Sequence))
{
if (node.CanRetrieveADecoratorAndPerformHisTask())
{
executeType = node.Execute();
if (EBehaviourExecuteType.Success == executeType)
break;
}
}
//Autrement Si c'est une tâche je l'execute et stop l'arbre.
else if (node.IsOfType(EBehaviourNodeType.Task))
{
executeType = node.Execute();
if (EBehaviourExecuteType.Success == executeType)
break;
}
}
}
return executeType;
}
#endregion
}
/// <summary>
/// Noeud permettant de parcourir l'arbre de sorte à itérer sur tous ses enfants.
/// </summary>
public class BehaviourNodeSequence : ABehaviourNodeTraversal
{
#region Constructor
public BehaviourNodeSequence()
{
this.Type = EBehaviourNodeType.Sequence;
}
#endregion
#region Override Methods
/// <summary>
/// Find all node where the decorator can perform the task and perform them.
/// </summary>
public override EBehaviourExecuteType Execute()
{
EBehaviourExecuteType executeType = EBehaviourExecuteType.Fail;
if (null != this.Nodes)
{
foreach (ABehaviourNode node in this.Nodes)
{
//je récupére le décorateur et si il m'autorise à lancer les sous neouds, je les lance puis je stop l'arbre.
if (node.IsOfType(EBehaviourNodeType.Selector) || node.IsOfType(EBehaviourNodeType.Sequence))
{
if (node.CanRetrieveADecoratorAndPerformHisTask())
{
EBehaviourExecuteType executeTypeTmp = node.Execute();
if (EBehaviourExecuteType.Success == executeTypeTmp)
executeType = executeTypeTmp;
}
}
//Autrement Si c'est une tâche je l'execute et stop l'arbre.
else if (node.IsOfType(EBehaviourNodeType.Task))
{
EBehaviourExecuteType executeTypeTmp = node.Execute();
if (EBehaviourExecuteType.Success == executeTypeTmp)
executeType = executeTypeTmp;
}
}
}
return executeType;
}
#endregion
}
/// <summary>
/// Contient un behaviournode root permettant de lancer une action de façon récursive.
/// </summary>
public class BehaviourTree
{
#region Fields
private ABehaviourNode root;
private ABlackboard blackboard;
#endregion
#region Constructor
public BehaviourTree(ABehaviourNode root, ABlackboard blackboard)
{
this.root = root;
this.blackboard = blackboard;
this.root.Blackboard = blackboard;
}
#endregion
#region Behaviour
public void Execute()
{
if (null != this.root)
this.root.Execute();
if (null != this.blackboard)
this.blackboard.Update();
}
#endregion
}
public enum EBehaviourExecuteType
{
Success,
Fail,
}
public enum EBehaviourNodeType
{
NotDefine,
Selector,
Sequence,
Task,
}
// Root
// SelectorA
//// / \
// SequenceA task
// / \
// SelectorB SelectorC
// Decorator Decorator
// | |
// task task
#region Tests classes
public class BehaviourTreeTest : MonoBehaviour
{
private BehaviourTree behaviourTree;
void Start()
{
#region Creation de tous les différents noeuds et du blackboard
TaskPrintSelectorA taskSelectorA = new TaskPrintSelectorA();
TaskPrintSelectorB taskSelectorB = new TaskPrintSelectorB();
TaskPrintSelectorC taskSelectorC = new TaskPrintSelectorC();
BehaviourNodeSelector selectorA = new BehaviourNodeSelector();
BehaviourNodeSelector selectorB = new BehaviourNodeSelector();
BehaviourNodeSelector selectorC = new BehaviourNodeSelector();
BehaviourNodeSequence sequenceA = new BehaviourNodeSequence();
DecoratorSelectorB decoratorSelectorB = new DecoratorSelectorB();
DecoratorSelectorC decoratorSelectorC = new DecoratorSelectorC();
CustomBlackboard blackboard = new CustomBlackboard();
#endregion
#region Liaison des noeuds avec les autres
selectorA.Nodes = new ABehaviourNode[]
{
sequenceA,
taskSelectorA,
};
sequenceA.Nodes = new ABehaviourNode[]
{
selectorB,
selectorC,
};
selectorB.Nodes = new ABehaviourNode[]
{
taskSelectorB
};
selectorC.Nodes = new ABehaviourNode[]
{
taskSelectorC
};
#endregion
#region Liaison des decorators avec les noeuds
selectorB.Decorator = decoratorSelectorB;
selectorC.Decorator = decoratorSelectorC;
#endregion
behaviourTree = new BehaviourTree(selectorA, blackboard);
}
void Update()
{
behaviourTree.Execute();
Debug.Log("END");
}
}
#region Tasks
public class TaskPrintSelectorA : ABehaviourNodeTask
{
public override EBehaviourExecuteType Execute()
{
Debug.Log("Selector A");
return EBehaviourExecuteType.Success;
}
}
public class TaskPrintSelectorB : ABehaviourNodeTask
{
public override EBehaviourExecuteType Execute()
{
Debug.Log("Selector B");
return EBehaviourExecuteType.Success;
}
}
public class TaskPrintSelectorC : ABehaviourNodeTask
{
public override EBehaviourExecuteType Execute()
{
Debug.Log("Selector C");
return EBehaviourExecuteType.Success;
}
}
#endregion
#region Decorator
public class DecoratorSelectorB : ABehaviourNodeDecorator
{
public override bool CanPerformTask()
{
return false;
}
}
public class DecoratorSelectorC : ABehaviourNodeDecorator
{
public override bool CanPerformTask()
{
return true;
}
}
#endregion
public class CustomBlackboard : ABlackboard
{
/// <summary>
/// On devra mettre à jour ici notre classe de data pour quelle soit cohérente avec le temps qui passe et notre gameplay.
/// </summary>
public override void Update()
{
}
}
#endregion
//voir si on peut récupérer le blackboard dans un decorateur snas que ça segfualt.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment