diff --git a/Editor/Core/GOAPPlannerEditor.cs b/Editor/Core/GOAPPlannerEditor.cs
index 5925a0b..c09ba78 100644
--- a/Editor/Core/GOAPPlannerEditor.cs
+++ b/Editor/Core/GOAPPlannerEditor.cs
@@ -4,31 +4,31 @@
using UnityEngine.UIElements;
namespace Kurisu.GOAP.Editor
{
- [CustomEditor(typeof(GOAPPlanner),true)]
+ [CustomEditor(typeof(GOAPPlanner), true)]
public class GOAPPlannerEditor : UnityEditor.Editor
{
- private const string LabelText="AkiGOAP V1.0 Planner";
- private const string ButtonText="Open Planner Snapshot";
- private const string GraphButtonText="Open GOAP Editor";
+ private const string LabelText = "AkiGOAP V1.0 Planner";
+ private const string ButtonText = "Open Planner Snapshot";
+ private const string GraphButtonText = "Open GOAP Editor";
public override VisualElement CreateInspectorGUI()
{
var myInspector = new VisualElement();
- myInspector.Add(UIUtility.GetLabel(LabelText,20));
+ myInspector.Add(UIUtility.GetLabel(LabelText, 20));
InspectorElement.FillDefaultInspector(myInspector, serializedObject, this);
myInspector.Remove(myInspector.Q("PropertyField:m_Script"));
//Setting
myInspector.AddSpace();
- UIUtility.GetLabel("Normal Setting",14,color:UIUtility.AkiBlue,anchor:TextAnchor.MiddleLeft).AddTo(myInspector);
+ UIUtility.GetLabel("Normal Setting", 14, color: UIUtility.AkiBlue, anchor: TextAnchor.MiddleLeft).AddTo(myInspector);
myInspector.Q("PropertyField:logType").MoveToEnd(myInspector);
myInspector.Q("PropertyField:tickType").MoveToEnd(myInspector);
//SnapShot
- UIUtility.GetButton(ButtonText,UIUtility.AkiRed,ShowPlannerWindow,100)
+ UIUtility.GetButton(ButtonText, UIUtility.AkiRed, ShowPlannerWindow, 100)
.Enabled(Application.isPlaying)
.AddTo(myInspector);
//Editor
- UIUtility.GetButton(GraphButtonText,UIUtility.AkiBlue,ShowGOAPEditor,100)
+ UIUtility.GetButton(GraphButtonText, UIUtility.AkiBlue, ShowGOAPEditor, 100)
.Enabled(Application.isPlaying)
- .AddTo(myInspector);
+ .AddTo(myInspector);
return myInspector;
}
private void ShowPlannerWindow()
diff --git a/Editor/Core/GOAPPlannerProEditor.cs b/Editor/Core/GOAPPlannerProEditor.cs
index 2bb348b..024d3f3 100644
--- a/Editor/Core/GOAPPlannerProEditor.cs
+++ b/Editor/Core/GOAPPlannerProEditor.cs
@@ -4,42 +4,47 @@
using UnityEngine.UIElements;
namespace Kurisu.GOAP.Editor
{
- [CustomEditor(typeof(GOAPPlannerPro),true)]
+ [CustomEditor(typeof(GOAPPlannerPro), true)]
public class GOAPPlannerProEditor : UnityEditor.Editor
{
- private const string LabelText="AkiGOAP V1.0 Planner Pro";
- private const string ButtonText="Open Planner Snapshot";
- private const string GraphButtonText="Open GOAP Editor";
- private const string SkilSearchTooltip="Enabled to skip search plan when already have an action, toggle this will need you to set correct precondition"+
+ private const string LabelText = "AkiGOAP V1.0 Planner Pro";
+ private const string ButtonText = "Open Planner Snapshot";
+ private const string GraphButtonText = "Open GOAP Editor";
+ private const string SkilSearchTooltip = "Enabled to skip search plan when already have an action, toggle this will need you to set correct precondition" +
"for each action to let it quit by itself";
public override VisualElement CreateInspectorGUI()
{
var myInspector = new VisualElement();
- myInspector.Add(UIUtility.GetLabel(LabelText,20));
+ myInspector.Add(UIUtility.GetLabel(LabelText, 20));
//Default
InspectorElement.FillDefaultInspector(myInspector, serializedObject, this);
myInspector.Remove(myInspector.Q("PropertyField:m_Script"));
myInspector.Remove(myInspector.Q("PropertyField:skipSearchWhenActionRunning"));
+ myInspector.Remove(myInspector.Q("PropertyField:isActive"));
//Setting
myInspector.AddSpace();
- UIUtility.GetLabel("Normal Setting",14,color:UIUtility.AkiBlue,anchor:TextAnchor.MiddleLeft).AddTo(myInspector);
+ UIUtility.GetLabel("Normal Setting", 14, color: UIUtility.AkiBlue, anchor: TextAnchor.MiddleLeft).AddTo(myInspector);
myInspector.Q("PropertyField:logType").MoveToEnd(myInspector);
myInspector.Q("PropertyField:tickType").MoveToEnd(myInspector);
+ var isActive = new Toggle("Is Active");
+ isActive.tooltip = SkilSearchTooltip;
+ isActive.BindProperty(serializedObject.FindProperty("isActive"));
+ isActive.AddTo(myInspector);
myInspector.AddSpace();
- UIUtility.GetLabel("Pro Setting",14,color:UIUtility.AkiBlue,anchor:TextAnchor.MiddleLeft).AddTo(myInspector);
- var skipSearchProperty=serializedObject.FindProperty("skipSearchWhenActionRunning");
- var skipSearchToggle=new Toggle("Skip Search When Action Running");
- skipSearchToggle.tooltip=SkilSearchTooltip;
+ UIUtility.GetLabel("Pro Setting", 14, color: UIUtility.AkiBlue, anchor: TextAnchor.MiddleLeft).AddTo(myInspector);
+ var skipSearchProperty = serializedObject.FindProperty("skipSearchWhenActionRunning");
+ var skipSearchToggle = new Toggle("Skip Search When Action Running");
+ skipSearchToggle.tooltip = SkilSearchTooltip;
skipSearchToggle.BindProperty(skipSearchProperty);
skipSearchToggle.AddTo(myInspector);
//SnapShot
- UIUtility.GetButton(ButtonText,UIUtility.AkiRed,ShowPlannerWindow,100)
+ UIUtility.GetButton(ButtonText, UIUtility.AkiRed, ShowPlannerWindow, 100)
.Enabled(Application.isPlaying)
.AddTo(myInspector);
//Editor
- UIUtility.GetButton(GraphButtonText,UIUtility.AkiBlue,ShowGOAPEditor,100)
+ UIUtility.GetButton(GraphButtonText, UIUtility.AkiBlue, ShowGOAPEditor, 100)
.Enabled(Application.isPlaying)
- .AddTo(myInspector);
+ .AddTo(myInspector);
return myInspector;
}
private void ShowPlannerWindow()
diff --git a/Editor/Resources/Icons/action_icon.png.meta b/Editor/Resources/Icons/action_icon.png.meta
index 4b97b8b..4969ae0 100644
--- a/Editor/Resources/Icons/action_icon.png.meta
+++ b/Editor/Resources/Icons/action_icon.png.meta
@@ -6,7 +6,7 @@ TextureImporter:
serializedVersion: 12
mipmaps:
mipMapMode: 0
- enableMipMap: 1
+ enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
@@ -39,10 +39,10 @@ TextureImporter:
wrapU: 0
wrapV: 0
wrapW: 0
- nPOTScale: 1
+ nPOTScale: 0
lightmap: 0
compressionQuality: 50
- spriteMode: 0
+ spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
@@ -51,7 +51,7 @@ TextureImporter:
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
- alphaIsTransparency: 0
+ alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
@@ -63,7 +63,7 @@ TextureImporter:
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
- cookieLightType: 0
+ cookieLightType: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
@@ -119,7 +119,7 @@ TextureImporter:
outline: []
physicsShape: []
bones: []
- spriteID:
+ spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
diff --git a/Editor/Resources/Icons/goal_icon.png.meta b/Editor/Resources/Icons/goal_icon.png.meta
index ff36a7c..5e8e577 100644
--- a/Editor/Resources/Icons/goal_icon.png.meta
+++ b/Editor/Resources/Icons/goal_icon.png.meta
@@ -6,7 +6,7 @@ TextureImporter:
serializedVersion: 12
mipmaps:
mipMapMode: 0
- enableMipMap: 1
+ enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
@@ -39,10 +39,10 @@ TextureImporter:
wrapU: 0
wrapV: 0
wrapW: 0
- nPOTScale: 1
+ nPOTScale: 0
lightmap: 0
compressionQuality: 50
- spriteMode: 0
+ spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
@@ -51,7 +51,7 @@ TextureImporter:
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
- alphaIsTransparency: 0
+ alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
@@ -63,7 +63,7 @@ TextureImporter:
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
- cookieLightType: 0
+ cookieLightType: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
@@ -119,7 +119,7 @@ TextureImporter:
outline: []
physicsShape: []
bones: []
- spriteID:
+ spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
diff --git a/README.md b/README.md
index 93404a4..f368500 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,8 @@
AkiGOAP是一个支持可视化、模块化编辑,支持多线程的Goal Oriented Action Planner(目标导向的行为规划)Unity插件,同时集成了多个开源GOAP插件的功能。
-## 特点
+AkiGOAP is a Goal Oriented Action Planner unity plugin that supports visualization, modular editing, and multi-threading, which integrates the functions of multiple open source GOAP plugins.
+## 特点 Features
1. 多线程, 使用Unity Job System
@@ -22,12 +23,7 @@ AkiGOAP是一个支持可视化、模块化编辑,支持多线程的Goal Orien
-## 安装
-1. 从Release下载Package [Release Package](https://github.com/AkiKurisu/AkiGOAP/releases)
-2. 使用Unity Package Manager通过git URL下载 ```https://github.com/AkiKurisu/AkiGOAP.git```
-#
-
-## 如何使用
+## 如何使用 How To Use
由于GOAP AI的设计需要一定门槛,我只介绍如何使用插件的核心功能,具体的设计可以参考插件提供的Example样例。
@@ -80,7 +76,7 @@ AkiGOAP是一个支持可视化、模块化编辑,支持多线程的Goal Orien
8. 点击GOAPPlanner或GOAPPlannerPro的```Open GOAP Editor```打开编辑器查看当前所有Goal的Priority优先级和所有Action的Cost代价
9. 点击GOAPPlanner或GOAPPlannerPro的```Open Planner Snapshot```打开快照查看当前的Plan计划(即抵达当前Goal的一串Action序列)
-## 说明
+## 说明 Explanation
1. GOAPPlanner没有使用JobSystem,仅使用对象池减少了GC开销,适用于复杂度较低的任务,优化自 https://github.com/toastisme/OpenGOAP
特点:根据优先级最高的Goal搜索路径,没有搜索到则FallBack至下一优先级的Goal
diff --git a/Runtime/Component/GOAPPlannerPro.cs b/Runtime/Component/GOAPPlannerPro.cs
index 3a565f9..fae673a 100644
--- a/Runtime/Component/GOAPPlannerPro.cs
+++ b/Runtime/Component/GOAPPlannerPro.cs
@@ -11,8 +11,8 @@ namespace Kurisu.GOAP
/// Pro version using job system and burst compiler to support multi-thread
///
[RequireComponent(typeof(GOAPWorldState))]
- public class GOAPPlannerPro:MonoBehaviour,IPlanner
- {
+ public class GOAPPlannerPro : MonoBehaviour, IPlanner
+ {
[BurstCompile]
private struct GoalSorter : IComparer
{
@@ -22,145 +22,170 @@ public int Compare(IGoal x, IGoal y)
}
}
protected GOAPWorldState worldState;
- public GOAPWorldState WorldState=>worldState;
- protected readonly List goals=new();
- protected readonly List actions=new();
- public IGoal ActivateGoal{get; private set;}
- int IPlanner.activeActionIndex=>0;
+ public GOAPWorldState WorldState => worldState;
+ protected readonly List goals = new();
+ protected readonly List actions = new();
+ public IGoal ActivateGoal { get; private set; }
+ int IPlanner.activeActionIndex => 0;
private IAction candidateAction;
private IAction activateAction;
- private List candidatePlan=new();
- public IAction ActivateAction=>activateAction;
- public List ActivatePlan{get; private set;}=new();
+ private List candidatePlan = new();
+ public IAction ActivateAction => activateAction;
+ public List ActivatePlan { get; private set; } = new();
private IGoal candidateGoal;
- private List candidateGoals=new();
- internal List CandidateGoals=>candidateGoals;
+ private List candidateGoals = new();
+ internal List CandidateGoals => candidateGoals;
// Loggers
- [SerializeField,Tooltip("Control the log message of planner. Never: No log; OnlyActive: Only logging active plan message; IncludeSearch: Include "+
- "searching detail like action select information; IncludeFail: Include logging all fail message like fail to find path or fail to find a goal. "+
+ [SerializeField, Tooltip("Control the log message of planner. Never: No log; OnlyActive: Only logging active plan message; IncludeSearch: Include " +
+ "searching detail like action select information; IncludeFail: Include logging all fail message like fail to find path or fail to find a goal. " +
"Always: Log all message")]
private LogType logType;
- private bool logActive=>logType.HasFlag(LogType.OnlyActive);
- private bool logSearch=>logType.HasFlag(LogType.IncludeSearch);
- private bool logFail=>logType.HasFlag(LogType.IncludeFail);
- [SerializeField,Tooltip("Nothing: Automatically update planner.\n"+
- "ManualUpdateGoal: Toggle this to disable planner to tick goal automatically.\n"+
- "ManualActivatePlanner: Toggle this to disable planner to tick and search plan automatically,"+
+ private bool logActive => logType.HasFlag(LogType.OnlyActive);
+ private bool logSearch => logType.HasFlag(LogType.IncludeSearch);
+ private bool logFail => logType.HasFlag(LogType.IncludeFail);
+ [SerializeField, Tooltip("Nothing: Automatically update planner.\n" +
+ "ManualUpdateGoal: Toggle this to disable planner to tick goal automatically.\n" +
+ "ManualActivatePlanner: Toggle this to disable planner to tick and search plan automatically," +
" however when the plan is generated, the planner will focus that plan until the plan is deactivated. So you can't stop plan manually.")]
private TickType tickType;
[SerializeField]
private bool skipSearchWhenActionRunning;
- internal bool SkipSearchWhenActionRunning=>skipSearchWhenActionRunning;
- public List Behaviors=>Enumerable.Empty().Concat(actions.OfType()).Concat(goals.OfType()).ToList();
- public UnityEngine.Object _Object =>gameObject;
+ internal bool SkipSearchWhenActionRunning => skipSearchWhenActionRunning;
+ public List Behaviors => Enumerable.Empty().Concat(actions.OfType()).Concat(goals.OfType()).ToList();
+ public UnityEngine.Object _Object => gameObject;
public event System.Action OnUpdatePlanEvent;
private GOAPJobRunner jobRunner;
- private bool isDirty=false;
- public bool IsActive{get;private set;}=true;
- private void Awake() {
+ private bool isDirty = false;
+ [SerializeField]
+ private bool isActive;
+ private bool activateFlag = false;
+ private void Awake()
+ {
worldState = GetComponent();
- IsActive&=!tickType.HasFlag(TickType.ManualActivatePlanner);
+ isActive &= !tickType.HasFlag(TickType.ManualActivatePlanner);
}
- private void Update(){
- if(isDirty)
+ private void Update()
+ {
+ if (activateFlag)
+ {
+ isActive = true;
+ activateFlag = false;
+ }
+ if (isDirty)
{
- isDirty=false;
+ isDirty = false;
jobRunner?.Dispose();
- jobRunner=new GOAPJobRunner(this,new GraphResolver(actions.Cast().Concat(goals)));
+ jobRunner = new GOAPJobRunner(this, new GraphResolver(actions.Cast().Concat(goals)));
}
- if(IsActive)Tick();
+ if (isActive) Tick();
}
public void ManualActivate()
{
- IsActive=true;
+ //Ensure jobRunner activate in Update()
+ activateFlag = true;
}
- private void LateUpdate() {
- if(!IsActive)return;
+ private void LateUpdate()
+ {
+ if (!isActive) return;
//Ensure job is completed
jobRunner?.Complete();
- bool debugUpdate=false;
- if ((NoActiveGoal() && CandidateGoalAvailable()) || BetterGoalAvailable()){
+ bool debugUpdate = false;
+ if ((NoActiveGoal() && CandidateGoalAvailable()) || BetterGoalAvailable())
+ {
StartCurrentBestGoal();
- debugUpdate=true;
+ debugUpdate = true;
}
- else if(HaveNextAction())
+ else if (HaveNextAction())
{
StartCurrentBestAction();
- debugUpdate=true;
+ debugUpdate = true;
}
- if(debugUpdate)
+ if (debugUpdate)
OnUpdatePlanEvent?.Invoke(this);
else
{
- if(tickType.HasFlag(TickType.ManualActivatePlanner))
+ if (tickType.HasFlag(TickType.ManualActivatePlanner))
{
- IsActive=false;
- if(logSearch)PlannerLog("Manual plan updating ends, need to be activated manully again.",bold:true);
+ isActive = false;
+ if (logSearch) PlannerLog("Manual plan updating ends, need to be activated manully again.", bold: true);
}
}
}
- private void OnDestroy() {
+ private void OnDestroy()
+ {
jobRunner?.Dispose();
}
void IPlanner.InjectGoals(IEnumerable source)
{
goals.Clear();
- foreach(var goal in source)
+ foreach (var goal in source)
{
goals.Add(goal);
goal.Init(worldState);
}
- isDirty=true;
+ isDirty = true;
}
void IPlanner.InjectActions(IEnumerable source)
{
actions.Clear();
- foreach(var action in source)
+ foreach (var action in source)
{
actions.Add(action);
action.Init(worldState);
}
- isDirty=true;
+ isDirty = true;
}
- internal void SetCandidate(List path,IGoal goal)
+ internal void SetCandidate(List path, IGoal goal)
{
- var action=path[0];
+ if (goal == null || path.Count == 0)
+ {
+ candidatePlan.Clear();
+ candidateAction = null;
+ candidateGoal = null;
+ if (logFail) PlannerLog("No candiate goal or path was found.");
+ return;
+ }
+ var action = path[0];
candidatePlan.Clear();
candidatePlan.AddRange(path);
- if(candidateAction!=action&&logSearch)PlannerLog($"Search candidate action:{action.Name}");
- candidateAction=action;
- if(candidateGoal!=goal&&logSearch)PlannerLog($"Search candidate goal:{goal.Name}");
- candidateGoal=goal;
+ if (candidateAction != action && logSearch) PlannerLog($"Search candidate action:{action.Name}");
+ candidateAction = action;
+ if (candidateGoal != goal && logSearch) PlannerLog($"Search candidate goal:{goal.Name}");
+ candidateGoal = goal;
}
- List IPlanner.GetAllActions()=>actions;
+ List IPlanner.GetAllActions() => actions;
public void Tick()
{
- if(!tickType.HasFlag(TickType.ManualUpdateGoal))TickGoals();
+ if (!tickType.HasFlag(TickType.ManualUpdateGoal)) TickGoals();
OnTickActivePlan();
GetHighestPriorityGoals(candidateGoals);
jobRunner?.Run();
}
- private bool NoActiveGoal(){
+ private bool NoActiveGoal()
+ {
return ActivateGoal == null;
}
private bool HaveNextAction()
{
- return candidateAction !=null && candidateAction != ActivateAction;
+ return candidateAction != null && candidateAction != ActivateAction;
}
- private bool BetterGoalAvailable(){
+ private bool BetterGoalAvailable()
+ {
return candidateGoal != null && candidateGoal != ActivateGoal;
}
- private bool CandidateGoalAvailable(){
+ private bool CandidateGoalAvailable()
+ {
return candidateAction != null && candidateGoal != null;
}
- private void StartCurrentBestGoal(){
+ private void StartCurrentBestGoal()
+ {
//Activate Goal
ActivateGoal?.OnDeactivate();
ActivateGoal = candidateGoal;
- if(logActive)ActivePlanLog($"Starting new plan for {ActivateGoal.Name}", bold:true);
+ if (logActive) ActivePlanLog($"Starting new plan for {ActivateGoal.Name}", bold: true);
ActivateGoal.OnActivate();
//Activate Action
StartCurrentBestAction();
@@ -169,47 +194,55 @@ private void StartCurrentBestAction()
{
ActivateAction?.OnDeactivate();
SetCurrentAction(candidateAction);
- if(logActive)ActivePlanLog($"Starting {ActivateAction.Name}");
+ if (logActive) ActivePlanLog($"Starting {ActivateAction.Name}");
ActivateAction.OnActivate();
}
- public void TickGoals(){
- if (goals != null){
- for (int i = 0; i < goals.Count; i++){
+ public void TickGoals()
+ {
+ if (goals != null)
+ {
+ for (int i = 0; i < goals.Count; i++)
+ {
goals[i].OnTick();
}
}
}
- private void OnTickActivePlan(){
+ private void OnTickActivePlan()
+ {
// Nothing to run
- if (ActivateGoal == null || ActivateAction == null){ return; }
+ if (ActivateGoal == null || ActivateAction == null) { return; }
// Goal no longer viable
- if (!ActivateGoal.PreconditionsSatisfied(worldState)){
- if(logActive)ActivePlanLog(
+ if (!ActivateGoal.PreconditionsSatisfied(worldState))
+ {
+ if (logActive) ActivePlanLog(
$"{ActivateGoal.Name} failed as preconditions are no longer satisfied",
- bold:true
+ bold: true
);
OnCompleteOrFailActivePlan();
return;
}
// Plan no longer viable
- if (!(ActivateAction.PreconditionsSatisfied(worldState))){
- if(logActive)ActivePlanLog(
+ if (!(ActivateAction.PreconditionsSatisfied(worldState)))
+ {
+ if (logActive) ActivePlanLog(
$"{ActivateAction.Name} failed as preconditions are no longer satisfied",
- bold:true
+ bold: true
);
- OnCompleteOrFailActivePlan();
+ OnCompleteOrFailActivePlan();
return;
}
ActivateAction.OnTick();
// Goal complete
- if (ActivateGoal.ConditionsSatisfied(worldState)){
- if(logActive)ActivePlanLog($"{ActivateGoal.Name} completed", bold:true);
+ if (ActivateGoal.ConditionsSatisfied(worldState))
+ {
+ if (logActive) ActivePlanLog($"{ActivateGoal.Name} completed", bold: true);
OnCompleteOrFailActivePlan();
return;
}
}
- private void OnCompleteOrFailActivePlan(){
+ private void OnCompleteOrFailActivePlan()
+ {
ActivateAction?.OnDeactivate();
ActivateGoal?.OnDeactivate();
ActivateGoal = null;
@@ -218,8 +251,8 @@ private void OnCompleteOrFailActivePlan(){
private void SetCurrentAction(IAction action)
{
ActivatePlan.Clear();
- activateAction=action;
- if(activateAction!=null)
+ activateAction = action;
+ if (activateAction != null)
ActivatePlan.AddRange(candidatePlan);
}
///
@@ -227,17 +260,21 @@ private void SetCurrentAction(IAction action)
/// has a valid plan
///
///
- private void GetHighestPriorityGoals(List chosenGoals){
+ private void GetHighestPriorityGoals(List chosenGoals)
+ {
chosenGoals.Clear();
//Searching for highest priority goal
- if (goals == null||goals.Count==0){
- if(logFail)PlannerLog("No goals found");
+ if (goals == null || goals.Count == 0)
+ {
+ if (logFail) PlannerLog("No goals found");
return;
}
- for (int i = 0; i < goals.Count; i++){
+ for (int i = 0; i < goals.Count; i++)
+ {
- if (!goals[i].PreconditionsSatisfied(worldState)){
- if(logFail)PlannerLog($"{goals[i].Name} not valid as preconditions not satisfied");
+ if (!goals[i].PreconditionsSatisfied(worldState))
+ {
+ if (logFail) PlannerLog($"{goals[i].Name} not valid as preconditions not satisfied");
continue;
}
chosenGoals.Add(goals[i]);
@@ -251,7 +288,8 @@ private void GetHighestPriorityGoals(List chosenGoals){
List IPlanner.GetSortedGoalData()
{
List goalData = new List();
- for (int i=0; i IPlanner.GetSortedGoalData()
goalData.Reverse();
return goalData;
}
- private void ActivePlanLog(object message, bool bold=false){
+ private void ActivePlanLog(object message, bool bold = false)
+ {
string s = $"ActivePlan: {message}";
- if (bold){
+ if (bold)
+ {
s = "" + s + "";
}
- Debug.Log(s,this);
+ Debug.Log(s, this);
}
- private void PlannerLog(object message, bool bold=false){
+ private void PlannerLog(object message, bool bold = false)
+ {
string s = $"Planner: {message}";
- if (bold){
+ if (bold)
+ {
s = "" + s + "";
}
- Debug.Log(s,this);
+ Debug.Log(s, this);
}
}
}
\ No newline at end of file
diff --git a/Runtime/Interface/IPlanner.cs b/Runtime/Interface/IPlanner.cs
index cd435bd..9ba82b8 100644
--- a/Runtime/Interface/IPlanner.cs
+++ b/Runtime/Interface/IPlanner.cs
@@ -5,12 +5,14 @@ public interface IPlanner : IGOAPSet
{
void InjectGoals(IEnumerable source);
void InjectActions(IEnumerable source);
- IGoal ActivateGoal{get;}
- List ActivatePlan{get;}
+ IGoal ActivateGoal { get; }
+ List ActivatePlan { get; }
List GetAllActions();
event System.Action OnUpdatePlanEvent;
- GOAPWorldState WorldState{get;}
- int activeActionIndex{get;}
+ GOAPWorldState WorldState { get; }
+ int activeActionIndex { get; }
List GetSortedGoalData();
+ void TickGoals();
+ void ManualActivate();
}
}
diff --git a/Runtime/Runner/GoapJobRunner.cs b/Runtime/Runner/GoapJobRunner.cs
index f36dda1..9c393e5 100644
--- a/Runtime/Runner/GoapJobRunner.cs
+++ b/Runtime/Runner/GoapJobRunner.cs
@@ -14,12 +14,12 @@ public class GOAPJobRunner
private readonly IPositionBuilder positionBuilder;
private readonly ICostBuilder costBuilder;
private readonly IConditionBuilder conditionBuilder;
- private List resultCache=new();
+ private List resultCache = new();
public GOAPJobRunner(GOAPPlannerPro planner, IGraphResolver graphResolver)
{
this.planner = planner;
this.resolver = graphResolver;
-
+
this.executableBuilder = this.resolver.GetExecutableBuilder();
this.positionBuilder = this.resolver.GetPositionBuilder();
this.costBuilder = this.resolver.GetCostBuilder();
@@ -28,33 +28,33 @@ public GOAPJobRunner(GOAPPlannerPro planner, IGraphResolver graphResolver)
public void Run()
{
- this.resolveHandles.Clear();
+ this.resolveHandles.Clear();
RunInternal(planner);
}
private void RunInternal(GOAPPlannerPro planner)
{
- if (planner==null)
+ if (planner == null)
return;
- if(planner.CandidateGoals.Count==0)
+ if (planner.CandidateGoals.Count == 0)
return;
- if(planner.ActivateAction!=null&&planner.SkipSearchWhenActionRunning)
+ if (planner.ActivateAction != null && planner.SkipSearchWhenActionRunning)
return;
- this.FillBuilders(planner,planner.transform);
+ this.FillBuilders(planner, planner.transform);
//Create job for each candidate goal
- foreach(var goal in planner.CandidateGoals)
- this.resolveHandles.Add(new JobRunHandle(planner,goal,this.resolver.StartResolve(new RunData
- {
- StartIndex = this.resolver.GetIndex(goal),
- IsExecutable = new NativeArray(this.executableBuilder.Build(), Allocator.TempJob),
- Positions = new NativeArray(this.positionBuilder.Build(), Allocator.TempJob),
- Costs = new NativeArray(this.costBuilder.Build(), Allocator.TempJob),
- ConditionsMet = new NativeArray(this.conditionBuilder.Build(), Allocator.TempJob),
- DistanceMultiplier = 1f
- })));
+ foreach (var goal in planner.CandidateGoals)
+ this.resolveHandles.Add(new JobRunHandle(goal, this.resolver.StartResolve(new RunData
+ {
+ StartIndex = this.resolver.GetIndex(goal),
+ IsExecutable = new NativeArray(this.executableBuilder.Build(), Allocator.TempJob),
+ Positions = new NativeArray(this.positionBuilder.Build(), Allocator.TempJob),
+ Costs = new NativeArray(this.costBuilder.Build(), Allocator.TempJob),
+ ConditionsMet = new NativeArray(this.conditionBuilder.Build(), Allocator.TempJob),
+ DistanceMultiplier = 1f
+ })));
}
- private void FillBuilders(IPlanner agent,Transform transform)
+ private void FillBuilders(IPlanner agent, Transform transform)
{
this.executableBuilder.Clear();
this.positionBuilder.Clear();
@@ -62,10 +62,10 @@ private void FillBuilders(IPlanner agent,Transform transform)
foreach (var node in agent.GetAllActions())
{
- var allMet = true;
+ var allMet = true;
foreach (var condition in node.Conditions)
{
- if (!agent.WorldState.InSet(condition.Key,condition.Value))
+ if (!agent.WorldState.InSet(condition.Key, condition.Value))
{
allMet = false;
continue;
@@ -74,15 +74,15 @@ private void FillBuilders(IPlanner agent,Transform transform)
}
this.executableBuilder.SetExecutable(node, allMet);
this.costBuilder.SetCost(node, node.GetCost());
- this.positionBuilder.SetPosition(node, agent.WorldState.ResolveNodeTarget(node)?.position??transform.position);
+ this.positionBuilder.SetPosition(node, agent.WorldState.ResolveNodeTarget(node)?.position ?? transform.position);
}
}
public void Complete()
{
- bool find=false;
+ bool find = false;
foreach (var resolveHandle in this.resolveHandles)
{
- if(find)
+ if (find)
{
//Already search a plan, just complete
resolveHandle.Handle.CompleteNonAlloc(ref resultCache);
@@ -90,17 +90,19 @@ public void Complete()
}
resultCache.Clear();
resolveHandle.Handle.CompleteNonAlloc(ref resultCache);
- if (resolveHandle.Agent==null)
+ if (planner == null)
continue;
- if(resultCache.Count!=0)
+ if (resultCache.Count != 0)
{
//Get candidate goal and action with highest priority
- resolveHandle.Agent.SetCandidate(resultCache,resolveHandle.Goal);
+ planner.SetCandidate(resultCache, resolveHandle.Goal);
//If not find, thus fall back to next handle
- find=true;
+ find = true;
}
}
this.resolveHandles.Clear();
+ if (!find)
+ planner.SetCandidate(resultCache, null);
}
public void Dispose()
@@ -109,20 +111,18 @@ public void Dispose()
{
resolveHandle.Handle.CompleteNonAlloc(ref resultCache);
}
-
+
this.resolver.Dispose();
}
private struct JobRunHandle
{
- public GOAPPlannerPro Agent { get; }
- public IGoal Goal{get;}
+ public IGoal Goal { get; }
public IResolveHandle Handle { get; set; }
- public JobRunHandle(GOAPPlannerPro agent,IGoal goal,IResolveHandle handle)
+ public JobRunHandle(IGoal goal, IResolveHandle handle)
{
- this.Agent = agent;
this.Goal = goal;
- this.Handle=handle;
+ this.Handle = handle;
}
}
}