From 023884e7bfbfc40ed40ce944d63d85180612f486 Mon Sep 17 00:00:00 2001
From: AkiKurisu <111233991+AkiKurisu@users.noreply.github.com>
Date: Tue, 5 Sep 2023 14:16:49 +0800
Subject: [PATCH] Update and Fix
1.Add AssemblyInfo
2.GOAPWorldState now refresh states automatically
3.Fix some interface field name by Roslyn Analyzer
4. Update Example.unity Scene
5. Fix wrong tooltip for IsActivate field in PlannerPro
---
Editor/Core/GOAPActionSetEditor.cs | 10 +-
Editor/Core/GOAPGoalSetEditor.cs | 10 +-
Editor/Core/GOAPPlannerEditor.cs | 2 +-
Editor/Core/GOAPPlannerProEditor.cs | 8 +-
.../Core/GOAPPlannerSnapshotEditorWindow.cs | 172 +++---
Editor/Core/GOAPWorldStateEditor.cs | 82 +--
Editor/Core/GUIProperties.cs | 270 +++++----
Editor/Utility/UIUtility.cs | 56 +-
Editor/Window/GOAPEditorWindow.cs | 6 +-
Editor/Window/GOAPView.cs | 2 +-
Example/Scene/AIExample.unity | 571 +++++++++++++++++-
Example/Scripts/Action/GoToHome.cs | 8 +-
Example/Scripts/Action/GoToPlayer.cs | 2 +-
Example/Scripts/Action/GoToTent.cs | 8 +-
Example/Scripts/Action/HaveARest.cs | 26 +-
Example/Scripts/Action/Idle.cs | 4 +-
Example/Scripts/Goal/ExampleGoal.cs | 2 +-
Example/Scripts/Goal/FollowPlayerGoal.cs | 16 +-
Example/Scripts/Goal/RestoreEnergyGoal.cs | 14 +-
Runtime/AssemblyInfo.cs | 2 +
Runtime/AssemblyInfo.cs.meta | 11 +
Runtime/Component/GOAPPlanner.cs | 431 +++++++------
Runtime/Component/GOAPPlannerPro.cs | 42 +-
Runtime/Component/GOAPWorldState.cs | 71 ++-
Runtime/Config/GOAPActionSet.cs | 25 +-
Runtime/Config/GOAPGoalSet.cs | 25 +-
Runtime/Config/GOAPSet.cs | 31 +-
Runtime/Config/GOAPStateSet.cs | 44 +-
Runtime/Interface/IAction.cs | 12 +-
Runtime/Interface/IGOAPSet.cs | 4 +-
Runtime/Interface/IGoal.cs | 8 +-
Runtime/Interface/IPlanner.cs | 2 +-
Runtime/Model/GOAPAction.cs | 89 +--
Runtime/Model/GOAPBehavior.cs | 10 +-
Runtime/Model/GOAPGoal.cs | 41 +-
Runtime/Model/GOAPState.cs | 14 +-
Runtime/Resolver/GraphBuilder.cs | 36 +-
Runtime/Resolver/Interface/INode.cs | 4 +-
Runtime/Resolver/Model/Node.cs | 2 +-
Runtime/Runner/GoapJobRunner.cs | 52 +-
40 files changed, 1477 insertions(+), 748 deletions(-)
create mode 100644 Runtime/AssemblyInfo.cs
create mode 100644 Runtime/AssemblyInfo.cs.meta
diff --git a/Editor/Core/GOAPActionSetEditor.cs b/Editor/Core/GOAPActionSetEditor.cs
index 9bf089f..d53c46e 100644
--- a/Editor/Core/GOAPActionSetEditor.cs
+++ b/Editor/Core/GOAPActionSetEditor.cs
@@ -6,17 +6,17 @@ namespace Kurisu.GOAP.Editor
[CustomEditor(typeof(GOAPActionSet))]
public class GOAPActionSetEditor : UnityEditor.Editor
{
- private const string LabelText="AkiGOAP V1.0 ActionSet";
- private const string ButtonText="Open GOAP Editor";
+ private const string LabelText = "AkiGOAP V1.1 ActionSet";
+ private const string ButtonText = "Open GOAP Editor";
public override VisualElement CreateInspectorGUI()
{
var myInspector = new VisualElement();
myInspector.styleSheets.Add(UIUtility.GetInspectorStyleSheet());
- myInspector.Add(UIUtility.GetLabel(LabelText,20));
- var description=new PropertyField(serializedObject.FindProperty("Description"),string.Empty);
+ myInspector.Add(UIUtility.GetLabel(LabelText, 20));
+ var description = new PropertyField(serializedObject.FindProperty("Description"), string.Empty);
myInspector.Add(description);
//Draw Button
- myInspector.Add(UIUtility.GetButton(ButtonText,UIUtility.AkiBlue,Open,100));
+ myInspector.Add(UIUtility.GetButton(ButtonText, UIUtility.AkiBlue, Open, 100));
return myInspector;
}
private void Open()
diff --git a/Editor/Core/GOAPGoalSetEditor.cs b/Editor/Core/GOAPGoalSetEditor.cs
index 7d3d581..bbb1eb0 100644
--- a/Editor/Core/GOAPGoalSetEditor.cs
+++ b/Editor/Core/GOAPGoalSetEditor.cs
@@ -7,17 +7,17 @@ namespace Kurisu.GOAP.Editor
[CustomEditor(typeof(GOAPGoalSet))]
public class GOAPGoalSetEditor : UnityEditor.Editor
{
- private const string LabelText="AkiGOAP V1.0 GoalSet";
- private const string ButtonText="Open GOAP Editor";
+ private const string LabelText = "AkiGOAP V1.1 GoalSet";
+ private const string ButtonText = "Open GOAP Editor";
public override VisualElement CreateInspectorGUI()
{
var myInspector = new VisualElement();
myInspector.styleSheets.Add(UIUtility.GetInspectorStyleSheet());
- myInspector.Add(UIUtility.GetLabel(LabelText,20));
- var description=new PropertyField(serializedObject.FindProperty("Description"),string.Empty);
+ myInspector.Add(UIUtility.GetLabel(LabelText, 20));
+ var description = new PropertyField(serializedObject.FindProperty("Description"), string.Empty);
myInspector.Add(description);
//Draw Button
- myInspector.Add(UIUtility.GetButton(ButtonText,UIUtility.AkiBlue,Open,100));
+ myInspector.Add(UIUtility.GetButton(ButtonText, UIUtility.AkiBlue, Open, 100));
return myInspector;
}
private void Open()
diff --git a/Editor/Core/GOAPPlannerEditor.cs b/Editor/Core/GOAPPlannerEditor.cs
index c09ba78..f8991f3 100644
--- a/Editor/Core/GOAPPlannerEditor.cs
+++ b/Editor/Core/GOAPPlannerEditor.cs
@@ -7,7 +7,7 @@ namespace Kurisu.GOAP.Editor
[CustomEditor(typeof(GOAPPlanner), true)]
public class GOAPPlannerEditor : UnityEditor.Editor
{
- private const string LabelText = "AkiGOAP V1.0 Planner";
+ private const string LabelText = "AkiGOAP V1.1 Planner";
private const string ButtonText = "Open Planner Snapshot";
private const string GraphButtonText = "Open GOAP Editor";
public override VisualElement CreateInspectorGUI()
diff --git a/Editor/Core/GOAPPlannerProEditor.cs b/Editor/Core/GOAPPlannerProEditor.cs
index 09dfa11..d59e0c1 100644
--- a/Editor/Core/GOAPPlannerProEditor.cs
+++ b/Editor/Core/GOAPPlannerProEditor.cs
@@ -7,10 +7,12 @@ namespace Kurisu.GOAP.Editor
[CustomEditor(typeof(GOAPPlannerPro), true)]
public class GOAPPlannerProEditor : UnityEditor.Editor
{
- private const string LabelText = "AkiGOAP V1.0 Planner Pro";
+ private const string LabelText = "AkiGOAP V1.1 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 IsActiveTooltip = "Whether current planner is active, will be disbled automatically" +
+ " when skipSearchWhenActionRunning is on";
+ private const string SkilSearchTooltip = "Enabled to skip search plan when already have an action, enable this will need you to set correct precondition" +
"for each action to let it quit by itself";
public override VisualElement CreateInspectorGUI()
{
@@ -28,7 +30,7 @@ public override VisualElement CreateInspectorGUI()
myInspector.Q("PropertyField:tickType").MoveToEnd(myInspector);
var isActive = new Toggle("Is Active")
{
- tooltip = SkilSearchTooltip
+ tooltip = IsActiveTooltip
};
isActive.BindProperty(serializedObject.FindProperty("isActive"));
isActive.AddTo(myInspector);
diff --git a/Editor/Core/GOAPPlannerSnapshotEditorWindow.cs b/Editor/Core/GOAPPlannerSnapshotEditorWindow.cs
index 958aec6..39be8d7 100644
--- a/Editor/Core/GOAPPlannerSnapshotEditorWindow.cs
+++ b/Editor/Core/GOAPPlannerSnapshotEditorWindow.cs
@@ -19,8 +19,8 @@ public class GOAPPlannerSnapshotEditorWindow : EditorWindow
// Active plan node params
private Vector2 nodeSpacing;
- private Vector2 nodeSize;
- private Vector2 taskNodeSize;
+ private Vector2 nodeSize;
+ private Vector2 taskNodeSize;
// Styles
private GUIStyle guiNodeStyle;
@@ -48,13 +48,13 @@ public class GOAPPlannerSnapshotEditorWindow : EditorWindow
private Color defaultTint;
private Color linkColor;
private Color panelColor;
-
- private readonly List planeCaches=new List();
+
+ private readonly List planeCaches = new List();
private int currentIndex;
- private List goalData=>planeCaches[currentIndex].goalData;
- private List activePlan=>planeCaches[currentIndex].activePlan;
- private IGoal activeGoal=>planeCaches[currentIndex].activeGoal;
- private int activeActionIdx=>planeCaches[currentIndex].activeActionIdx;
+ private List goalData => planeCaches[currentIndex].goalData;
+ private List activePlan => planeCaches[currentIndex].activePlan;
+ private IGoal activeGoal => planeCaches[currentIndex].activeGoal;
+ private int activeActionIdx => planeCaches[currentIndex].activeActionIdx;
private class PlanCache
{
public IGoal activeGoal;
@@ -62,8 +62,9 @@ private class PlanCache
public List activePlan;
public int activeActionIdx;
}
- public static void Show(IPlanner planner){
- var window=GOAPPlannerSnapshotEditorWindow.GetWindow("GOAP Planner Snapshot");
+ public static void Show(IPlanner planner)
+ {
+ var window = GOAPPlannerSnapshotEditorWindow.GetWindow("GOAP Planner Snapshot");
window.SetUp(planner);
window.Show();
}
@@ -78,34 +79,38 @@ private void OnPlayModeStateChanged(PlayModeStateChange playModeStateChange)
}
public void SetUp(IPlanner planner)
{
- this.planner=planner;
- planner.OnUpdatePlanEvent+=CachePlan;
+ this.planner = planner;
+ planner.OnUpdatePlanEvent += CachePlan;
}
- private void OnDestroy() {
- planner.OnUpdatePlanEvent-=CachePlan;
+ private void OnDestroy()
+ {
+ planner.OnUpdatePlanEvent -= CachePlan;
}
- private void SetupPanels(){
+ private void SetupPanels()
+ {
activePlanPanel = new Rect(
0,
0,
position.width,
- position.height*.4f
+ position.height * .4f
);
goalPrioritiesPanel = new Rect(
0,
- position.height*.4f,
+ position.height * .4f,
position.width,
position.height
);
}
- private void SetupActivePlanNodeParams(){
+ private void SetupActivePlanNodeParams()
+ {
nodeSpacing = GUIProperties.NodeSpacing();
nodeSize = GUIProperties.NodeSize();
taskNodeSize = GUIProperties.TaskNodeSize();
}
- private void SetupGUIStyles(){
+ private void SetupGUIStyles()
+ {
guiNodeStyle = GUIProperties.GUINodeStyle();
selectedNodeStyle = GUIProperties.SelectedGUINodeStyle();
activeNodeStyle = guiNodeStyle;
@@ -114,7 +119,8 @@ private void SetupGUIStyles(){
disabledGoalLabelStyle = GUIProperties.DisabledGoalLabelStyle();
}
- private void SetupColors(){
+ private void SetupColors()
+ {
backgroundNodeColor = GUIProperties.BackgroundNodeColor();
actionColor = GUIProperties.ActionColor();
goalColor = GUIProperties.GoalColor();
@@ -123,14 +129,17 @@ private void SetupColors(){
linkColor = GUIProperties.LinkColor();
panelColor = GUIProperties.PanelColor();
}
- private void SetupGUIContent(){
+ private void SetupGUIContent()
+ {
actionContent = GUIProperties.ActionContent();
goalContent = GUIProperties.GoalContent();
}
- private void SetupGoalPriorities(){
+ private void SetupGoalPriorities()
+ {
maxPriorityRectWidth = position.width - 10f;
}
- private void OnEnable(){
+ private void OnEnable()
+ {
SetupPanels();
SetupActivePlanNodeParams();
SetupGUIStyles();
@@ -149,19 +158,20 @@ private void CachePlan(IPlanner planner)
{
planeCaches.Add(new PlanCache
{
- activeGoal=planner.ActivateGoal,
+ activeGoal = planner.ActivateGoal,
goalData = (planner as IPlanner).GetSortedGoalData(),
- activePlan =new List(planner.ActivatePlan),
- activeActionIdx = (planner as IPlanner).activeActionIndex
+ activePlan = new List(planner.ActivatePlan),
+ activeActionIdx = (planner as IPlanner).ActiveActionIndex
});
- if(planeCaches.Count>50)planeCaches.RemoveAt(0);
+ if (planeCaches.Count > 50) planeCaches.RemoveAt(0);
}
Repaint();
}
- private void OnGUI(){
+ private void OnGUI()
+ {
DrawGrid(20, 0.2f, Color.gray);
DrawGrid(100, 0.4f, Color.gray);
-
+
DrawActivePlanPanel();
DrawGoalPrioritiesPanel();
DrawToolBar();
@@ -172,40 +182,41 @@ private void DrawToolBar()
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
- GUI.enabled=planeCaches.Count!=0;
+ GUI.enabled = planeCaches.Count != 0;
if (GUILayout.Button("Last", GUILayout.MinWidth(100)))
{
currentIndex--;
- if(currentIndex<0)currentIndex=planeCaches.Count-1;
+ if (currentIndex < 0) currentIndex = planeCaches.Count - 1;
Repaint();
}
- int total=Mathf.Max(1,planeCaches.Count);
- GUILayout.TextField($"Cache:{currentIndex+1}/{total}");
+ int total = Mathf.Max(1, planeCaches.Count);
+ GUILayout.TextField($"Cache:{currentIndex + 1}/{total}");
if (GUILayout.Button("Next", GUILayout.MinWidth(100)))
{
currentIndex++;
- if(currentIndex>=planeCaches.Count)currentIndex=0;
+ if (currentIndex >= planeCaches.Count) currentIndex = 0;
Repaint();
}
if (GUILayout.Button("Clear", GUILayout.MinWidth(100)))
{
- currentIndex=0;
+ currentIndex = 0;
planeCaches.Clear();
Repaint();
}
- GUI.enabled=true;
+ GUI.enabled = true;
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
}
- private void DrawActivePlanPanel(){
+ private void DrawActivePlanPanel()
+ {
GUI.color = runningTint;
GUI.backgroundColor = panelColor;
activePlanPanel = new Rect(
0,
0,
position.width,
- position.height*.4f
+ position.height * .4f
);
BeginWindows();
activePlanPanel = GUILayout.Window(
@@ -218,12 +229,13 @@ private void DrawActivePlanPanel(){
EndWindows();
}
- private void DrawGoalPrioritiesPanel(){
+ private void DrawGoalPrioritiesPanel()
+ {
GUI.color = runningTint;
GUI.backgroundColor = panelColor;
goalPrioritiesPanel = new Rect(
0,
- position.height*.3f,
+ position.height * .3f,
position.width,
position.height
);
@@ -239,15 +251,17 @@ private void DrawGoalPrioritiesPanel(){
}
- private bool IsActive(){
+ private bool IsActive()
+ {
return (
- planner != null
- && planner.ActivatePlan != null
- && planner.ActivatePlan.Count>0
+ planner != null
+ && planner.ActivatePlan != null
+ && planner.ActivatePlan.Count > 0
);
}
- private void DrawActivePlan(int unusedWindowID){
+ private void DrawActivePlan(int unusedWindowID)
+ {
DrawActionNodes(unusedWindowID);
}
///
@@ -255,21 +269,23 @@ private void DrawActivePlan(int unusedWindowID){
/// visualised as a progress bar
///
///
- private void DrawGoalPriorities(int unusedWindowID){
- if(planeCaches.Count==0)return;
+ private void DrawGoalPriorities(int unusedWindowID)
+ {
+ if (planeCaches.Count == 0) return;
GUILayout.Label("\n\n");
GUI.color = runningTint;
GUI.backgroundColor = backgroundNodeColor;
- for(int i=0; i
///
- private void DrawActionNodes(int unusedWindowID){
- if(planeCaches.Count==0)return;
+ private void DrawActionNodes(int unusedWindowID)
+ {
+ if (planeCaches.Count == 0) return;
int count = 0;
- for (int i = 0; i < activePlan.Count; i++){
+ for (int i = 0; i < activePlan.Count; i++)
+ {
// Draw link
- if (count > 0){
+ if (count > 0)
+ {
DrawLink(
- count-1,
+ count - 1,
count,
linkColor
);
@@ -294,16 +313,18 @@ private void DrawActionNodes(int unusedWindowID){
GUI.color = defaultTint;
GUI.backgroundColor = backgroundNodeColor;
- if (i==activeActionIdx){
+ if (i == activeActionIdx)
+ {
GUI.color = runningTint;
activeNodeStyle = selectedNodeStyle;
}
- else{
+ else
+ {
activeNodeStyle = guiNodeStyle;
}
// Draw node rect
GUI.Box(
- GetNodeRect(count),
+ GetNodeRect(count),
"",
activeNodeStyle);
@@ -312,8 +333,8 @@ private void DrawActionNodes(int unusedWindowID){
// Draw task rect
GUI.backgroundColor = actionColor;
GUI.Box(
- GetTaskRect(count),
- actionContent,
+ GetTaskRect(count),
+ actionContent,
activeNodeStyle);
count++;
@@ -321,26 +342,27 @@ private void DrawActionNodes(int unusedWindowID){
// Draw goal
DrawLink(
- count-1,
+ count - 1,
count,
linkColor
);
GUI.color = runningTint;
GUI.backgroundColor = backgroundNodeColor;
GUI.Box(
- GetNodeRect(count),
+ GetNodeRect(count),
"",
selectedNodeStyle);
GUI.backgroundColor = goalColor;
- goalContent.text = "\nGoal\n\n" +activeGoal.Name;
+ goalContent.text = "\nGoal\n\n" + activeGoal.Name;
GUI.Box(
- GetTaskRect(count),
- goalContent,
+ GetTaskRect(count),
+ goalContent,
selectedNodeStyle);
}
- private Rect GetNodeRect(int gridPos){
+ private Rect GetNodeRect(int gridPos)
+ {
return new Rect(
gridPos * nodeSpacing.x,
activePlanHeight,
@@ -349,10 +371,11 @@ private Rect GetNodeRect(int gridPos){
);
}
- private Rect GetTaskRect(int gridPos){
+ private Rect GetTaskRect(int gridPos)
+ {
return new Rect(
gridPos * nodeSpacing.x,
- activePlanHeight + taskNodeSize.y*.05f,
+ activePlanHeight + taskNodeSize.y * .05f,
taskNodeSize.x,
taskNodeSize.y
);
@@ -365,19 +388,20 @@ private Rect GetTaskRect(int gridPos){
///
///
private void DrawLink(
- int startGridPos,
- int endGridPos,
+ int startGridPos,
+ int endGridPos,
Color color,
- float thickness=4f){
+ float thickness = 4f)
+ {
Vector2 startPos = new Vector2(
startGridPos * nodeSpacing.x + nodeSize.x,
- activePlanHeight + nodeSize.y*.5f
+ activePlanHeight + nodeSize.y * .5f
);
Vector2 endPos = new Vector2(
endGridPos * nodeSpacing.x,
- activePlanHeight + nodeSize.y*.5f
+ activePlanHeight + nodeSize.y * .5f
);
Handles.DrawBezier(
@@ -407,7 +431,7 @@ private void DrawGrid(float gridSpacing, float gridOpacity, Color gridColor)
for (int i = 0; i < widthDivs; i++)
{
Handles.DrawLine(
- new Vector3(gridSpacing * i, -gridSpacing, 0),
+ new Vector3(gridSpacing * i, -gridSpacing, 0),
new Vector3(gridSpacing * i, position.height, 0f)
);
}
diff --git a/Editor/Core/GOAPWorldStateEditor.cs b/Editor/Core/GOAPWorldStateEditor.cs
index 8fd09ff..3db030e 100644
--- a/Editor/Core/GOAPWorldStateEditor.cs
+++ b/Editor/Core/GOAPWorldStateEditor.cs
@@ -6,72 +6,80 @@
using System.Reflection;
namespace Kurisu.GOAP.Editor
{
- [CustomEditor(typeof(GOAPWorldState),true)]
+ [CustomEditor(typeof(GOAPWorldState), true)]
public class GOAPWorldStateEditor : UnityEditor.Editor
{
- private const string LabelText="AkiGOAP V1.0 WorldState";
- private const string WorldState="Runtime States:";
- private const string True="True";
- private const string False="False";
- private const string ButtonText="Refresh States";
- private const string Global="Global";
- private const string Local="Local";
+ private const string LabelText = "AkiGOAP V1.1 WorldState";
+ private const string WorldState = "Runtime States:";
+ private const string True = "True";
+ private const string False = "False";
+ private const string Global = "Global";
+ private const string Local = "Local";
private VisualElement statesGroup;
- private static FieldInfo StatesInfo=typeof(GOAPStateSet).GetField("states",BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.Public);
+ private static readonly FieldInfo StatesInfo = typeof(GOAPStateSet).GetField("states", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
public override VisualElement CreateInspectorGUI()
{
var myInspector = new VisualElement();
var state = target as GOAPWorldState;
- myInspector.Add(UIUtility.GetLabel(LabelText,20));
- InspectorElement.FillDefaultInspector(myInspector, serializedObject, this);
+ myInspector.Add(UIUtility.GetLabel(LabelText, 20));
+ InspectorElement.FillDefaultInspector(myInspector, serializedObject, this);
myInspector.Remove(myInspector.Q("PropertyField:m_Script"));
- var localState=state?.LocalState;
- if(localState==null)return myInspector;
- var states = StatesInfo.GetValue(localState) as Dictionary;
- if(states==null||states.Count==0)return myInspector;
- var stateLabel=new Label(WorldState);
- stateLabel.style.fontSize=15;
+ var localState = state != null ? state.LocalState : null;
+ if (localState == null) return myInspector;
+ if (StatesInfo.GetValue(localState) is not Dictionary states || states.Count == 0) return myInspector;
+ var stateLabel = new Label(WorldState);
+ stateLabel.style.fontSize = 15;
myInspector.Add(stateLabel);
- statesGroup=new VisualElement();
+ statesGroup = new VisualElement();
myInspector.Add(statesGroup);
- var button=UIUtility.GetButton(ButtonText,UIUtility.AkiBlue,RefreshStates,100);
- button.SetEnabled(Application.isPlaying);
- myInspector.Add(button);
RefreshStates();
return myInspector;
}
- private void RefreshStates() {
+ private void OnEnable()
+ {
+ if (!Application.isPlaying) return;
+ var state = target as GOAPWorldState;
+ state.OnUpdate += RefreshStates;
+ }
+ private void OnDisable()
+ {
+ if (!Application.isPlaying) return;
+ var state = target as GOAPWorldState;
+ state.OnUpdate -= RefreshStates;
+ }
+ private void RefreshStates()
+ {
statesGroup.Clear();
var state = target as GOAPWorldState;
- var localState=state?.LocalState;
- if(localState!=null)
+ var localState = state != null ? state.LocalState : null;
+ if (localState != null)
{
var states = StatesInfo.GetValue(localState) as Dictionary;
- AddStates(states,false);
+ AddStates(states, false);
}
- var globalState=state?.GlobalState;
- if(globalState!=null)
+ var globalState = state != null ? state.GlobalState : null;
+ if (globalState != null)
{
var states = StatesInfo.GetValue(globalState) as Dictionary;
- AddStates(states,true);
+ AddStates(states, true);
}
}
- private void AddStates(Dictionarystates,bool global)
+ private void AddStates(Dictionary states, bool global)
{
- if(states==null||states.Count==0)return;
- foreach(var pair in states)
+ if (states == null || states.Count == 0) return;
+ foreach (var pair in states)
{
- var group=GetGroup();
- group.Add(UIUtility.GetLabel(global?Global:Local,12,33,UIUtility.AkiBlue));
- group.Add(UIUtility.GetLabel(pair.Key,12,33));
- group.Add(UIUtility.GetLabel(pair.Value?True:False,12,33));
+ var group = GetGroup();
+ group.Add(UIUtility.GetLabel(global ? Global : Local, 12, 33, UIUtility.AkiBlue));
+ group.Add(UIUtility.GetLabel(pair.Key, 12, 33));
+ group.Add(UIUtility.GetLabel(pair.Value ? True : False, 12, 33));
statesGroup.Add(group);
}
}
internal static VisualElement GetGroup()
{
- var group=new VisualElement();
- group.style.flexDirection=FlexDirection.Row;
+ var group = new VisualElement();
+ group.style.flexDirection = FlexDirection.Row;
return group;
}
}
diff --git a/Editor/Core/GUIProperties.cs b/Editor/Core/GUIProperties.cs
index 5965aa4..a7bead4 100644
--- a/Editor/Core/GUIProperties.cs
+++ b/Editor/Core/GUIProperties.cs
@@ -1,133 +1,149 @@
using UnityEngine;
using UnityEditor;
-namespace Kurisu.GOAP.Editor{
-public class GUIProperties
+namespace Kurisu.GOAP.Editor
{
- /**
- * Static methods to store general properties
- * for visualising GOAP components
- */
+ public class GUIProperties
+ {
+ /**
+ * Static methods to store general properties
+ * for visualising GOAP components
+ */
+
+ //// Sizes
+
+ public static Vector2 NodeSize() { return new Vector2(200, 90); }
+ public static Vector2 TaskNodeSize() { return new Vector2(200, 80); }
+
+ //// Positions
+
+ public static Vector2 NodeSpacing()
+ {
+ Vector2 guiNodeSize = GUIProperties.NodeSize();
+ int padding = 20;
+ return new Vector2(
+ guiNodeSize.x + padding,
+ guiNodeSize.y + padding
+ );
+ }
+
+ //// Colors
+
+ public static Color BackgroundNodeColor()
+ {
+ return new Color(87f / 255.0f, 117f / 255.0f, 144f / 255.0f);
+ }
+
+ public static Color GoalColor()
+ {
+ return new Color(0f, 1f, 1f, 0.9f);
+ }
+ public static Color ActionColor()
+ {
+ return new Color(251 / 255.0f, 114 / 255.0f, 153 / 255.0f, 0.9f);
+ }
+
+ public static Color PanelColor()
+ {
+ Color color = BackgroundNodeColor();
+ return new Color(color[0] * .5f, color[1] * .5f, color[2] * .5f);
+ }
+
+ public static Color RunningTint()
+ {
+ return new Color(1f, 1f, 1f);
+ }
+
+ public static Color DefaultTint()
+ {
+ return new Color(.8f, .8f, .8f);
+ }
+
+ public static Color LinkColor()
+ {
+ return Color.white;
+ }
+
+ //// Styles
+
+ public static GUIStyle GUINodeStyle()
+ {
+ GUIStyle nodeStyle = new GUIStyle();
+ nodeStyle.normal.background = EditorGUIUtility.Load(
+ "builtin skins/lightskin/images/node0.png"
+ ) as Texture2D;
+ nodeStyle.border = new RectOffset(12, 12, 12, 12);
+ nodeStyle.normal.textColor = Color.white;
+ nodeStyle.alignment = TextAnchor.UpperLeft;
+ nodeStyle.fontStyle = FontStyle.Bold;
+ nodeStyle.padding = new RectOffset(10, 0, 0, 0);
+ nodeStyle.fontSize = 14;
+ return nodeStyle;
+ }
+
+ public static GUIStyle SelectedGUINodeStyle()
+ {
+ GUIStyle selectedNodeStyle = GUINodeStyle();
+ selectedNodeStyle.normal.background = EditorGUIUtility.Load(
+ "builtin skins/lightskin/images/node0 on.png"
+ ) as Texture2D;
+ return selectedNodeStyle;
+ }
+
+ public static GUIStyle GUIPlannerStyle()
+ {
+ GUIStyle nodeStyle = new GUIStyle();
+ nodeStyle.normal.background = EditorGUIUtility.Load(
+ "builtin skins/lightskin/images/node0.png"
+ ) as Texture2D;
+ nodeStyle.border = new RectOffset(12, 12, 12, 12);
+ nodeStyle.normal.textColor = Color.white;
+ nodeStyle.alignment = TextAnchor.UpperLeft;
+ nodeStyle.fontStyle = FontStyle.Bold;
+ nodeStyle.padding = new RectOffset(10, 0, 10, 0);
+ nodeStyle.fontSize = 12;
+ return nodeStyle;
+ }
+
+ public static GUIStyle GoalLabelStyle()
+ {
+ GUIStyle nodeStyle = new GUIStyle();
+ nodeStyle.normal.background = EditorGUIUtility.Load(
+ "builtin skins/lightskin/images/node0.png"
+ ) as Texture2D;
+ nodeStyle.border = new RectOffset(12, 12, 12, 12);
+ nodeStyle.alignment = TextAnchor.MiddleLeft;
+ nodeStyle.normal.textColor = Color.white;
+ nodeStyle.fontStyle = FontStyle.Bold;
+ nodeStyle.padding = new RectOffset(10, 0, -5, 0);
+ nodeStyle.fontSize = 12;
+ return nodeStyle;
+ }
+
+ public static GUIStyle DisabledGoalLabelStyle()
+ {
+ GUIStyle nodeStyle = GoalLabelStyle();
+ nodeStyle.normal.textColor = Color.grey;
+ return nodeStyle;
+ }
+
+
+ //// GUIContent
+
+ public static GUIContent ActionContent()
+ {
+ Texture2D icon = Resources.Load("Icons/action_icon", typeof(Texture2D)) as Texture2D;
+ string text = "Action";
+ string tooltip = "Action for the gameobject to execute.";
+ return new GUIContent(text, icon, tooltip);
+ }
+
+ public static GUIContent GoalContent()
+ {
+ Texture2D icon = Resources.Load("Icons/goal_icon", typeof(Texture2D)) as Texture2D;
+ string text = "Goal";
+ string tooltip = "The goal this plan satisfies.";
+ return new GUIContent(text, icon, tooltip);
+ }
- //// Sizes
-
- public static Vector2 NodeSize(){return new Vector2(200, 90);}
- public static Vector2 TaskNodeSize(){return new Vector2(200, 80);}
-
- //// Positions
-
- public static Vector2 NodeSpacing(){
- Vector2 guiNodeSize = GUIProperties.NodeSize();
- int padding = 20;
- return new Vector2(
- guiNodeSize.x + padding,
- guiNodeSize.y + padding
- );
- }
-
- //// Colors
-
- public static Color BackgroundNodeColor(){
- return new Color(87f/255.0f, 117f/255.0f, 144f/255.0f);
- }
-
- public static Color GoalColor(){
- return new Color(0f, 1f, 1f,0.9f);
- }
- public static Color ActionColor(){
- return new Color(251/255.0f, 114/255.0f, 153/255.0f, 0.9f);
- }
-
- public static Color PanelColor(){
- Color color = BackgroundNodeColor();
- return new Color(color[0]*.5f, color[1]*.5f, color[2]*.5f);
- }
-
- public static Color RunningTint(){
- return new Color(1f, 1f, 1f);
- }
-
- public static Color DefaultTint(){
- return new Color(.8f, .8f, .8f);
- }
-
- public static Color LinkColor(){
- return Color.white;
}
-
- //// Styles
-
- public static GUIStyle GUINodeStyle(){
- GUIStyle nodeStyle = new GUIStyle();
- nodeStyle.normal.background = EditorGUIUtility.Load(
- "builtin skins/lightskin/images/node0.png"
- ) as Texture2D;
- nodeStyle.border = new RectOffset(12, 12, 12, 12);
- nodeStyle.normal.textColor = Color.white;
- nodeStyle.alignment = TextAnchor.UpperLeft;
- nodeStyle.fontStyle = FontStyle.Bold;
- nodeStyle.padding=new RectOffset(10,0,0,0);
- nodeStyle.fontSize=14;
- return nodeStyle;
- }
-
- public static GUIStyle SelectedGUINodeStyle(){
- GUIStyle selectedNodeStyle = GUINodeStyle();
- selectedNodeStyle.normal.background = EditorGUIUtility.Load(
- "builtin skins/lightskin/images/node0 on.png"
- ) as Texture2D;
- return selectedNodeStyle;
- }
-
- public static GUIStyle GUIPlannerStyle(){
- GUIStyle nodeStyle = new GUIStyle();
- nodeStyle.normal.background = EditorGUIUtility.Load(
- "builtin skins/lightskin/images/node0.png"
- ) as Texture2D;
- nodeStyle.border = new RectOffset(12, 12, 12, 12);
- nodeStyle.normal.textColor = Color.white;
- nodeStyle.alignment = TextAnchor.UpperLeft;
- nodeStyle.fontStyle = FontStyle.Bold;
- nodeStyle.padding=new RectOffset(10,0,10,0);
- nodeStyle.fontSize=12;
- return nodeStyle;
- }
-
- public static GUIStyle GoalLabelStyle(){
- GUIStyle nodeStyle = new GUIStyle();
- nodeStyle.normal.background = EditorGUIUtility.Load(
- "builtin skins/lightskin/images/node0.png"
- ) as Texture2D;
- nodeStyle.border = new RectOffset(12, 12, 12, 12);
- nodeStyle.alignment = TextAnchor.MiddleLeft;
- nodeStyle.normal.textColor = Color.white;
- nodeStyle.fontStyle = FontStyle.Bold;
- nodeStyle.padding=new RectOffset(10,0,-5,0);
- nodeStyle.fontSize=12;
- return nodeStyle;
- }
-
- public static GUIStyle DisabledGoalLabelStyle(){
- GUIStyle nodeStyle = GoalLabelStyle();
- nodeStyle.normal.textColor = Color.grey;
- return nodeStyle;
- }
-
-
- //// GUIContent
-
- public static GUIContent ActionContent(){
- Texture2D icon = Resources.Load("Icons/action_icon", typeof(Texture2D)) as Texture2D;
- string text = "Action";
- string tooltip = "Action for the gameobject to execute.";
- return new GUIContent(text, icon, tooltip);
- }
-
- public static GUIContent GoalContent(){
- Texture2D icon = Resources.Load("Icons/goal_icon", typeof(Texture2D)) as Texture2D;
- string text = "Goal";
- string tooltip = "The goal this plan satisfies.";
- return new GUIContent(text, icon, tooltip);
- }
-
-}
}
\ No newline at end of file
diff --git a/Editor/Utility/UIUtility.cs b/Editor/Utility/UIUtility.cs
index 5c6837c..8081a19 100644
--- a/Editor/Utility/UIUtility.cs
+++ b/Editor/Utility/UIUtility.cs
@@ -4,34 +4,34 @@ namespace Kurisu.GOAP.Editor
{
internal class UIUtility
{
- internal static Button GetButton(string text,Color? color=null,System.Action callBack=null,float widthPercent=50,float fontSize=15)
+ internal static Button GetButton(string text, Color? color = null, System.Action callBack = null, float widthPercent = 50, float fontSize = 15)
{
- var button=new Button();
- if(callBack!=null)
- button.clicked+=callBack;
- if(color.HasValue)
- button.style.backgroundColor=color.Value;
- button.style.width=Length.Percent(widthPercent);
- button.text=text;
- button.style.fontSize=fontSize;
+ var button = new Button();
+ if (callBack != null)
+ button.clicked += callBack;
+ if (color.HasValue)
+ button.style.backgroundColor = color.Value;
+ button.style.width = Length.Percent(widthPercent);
+ button.text = text;
+ button.style.fontSize = fontSize;
return button;
}
- internal static Label GetLabel(string text,int frontSize,float? widthPercent=null,Color? color=null,TextAnchor? anchor=TextAnchor.MiddleCenter)
+ internal static Label GetLabel(string text, int frontSize, float? widthPercent = null, Color? color = null, TextAnchor? anchor = TextAnchor.MiddleCenter)
{
- var label=new Label(text);
- label.style.fontSize=frontSize;
- if(widthPercent.HasValue)
- label.style.width=Length.Percent(widthPercent.Value);
- if(color.HasValue)
- label.style.color=color.Value;
- if(anchor.HasValue)
- label.style.unityTextAlign=anchor.Value;
+ var label = new Label(text);
+ label.style.fontSize = frontSize;
+ if (widthPercent.HasValue)
+ label.style.width = Length.Percent(widthPercent.Value);
+ if (color.HasValue)
+ label.style.color = color.Value;
+ if (anchor.HasValue)
+ label.style.unityTextAlign = anchor.Value;
return label;
}
- internal static Color AkiBlue=new Color(140/255f, 160/255f, 250/255f);
- internal static Color AkiRed=new Color(253/255f, 163/255f, 255/255f);
- private const string InspectorStyleSheetPath="AkiGOAP/Inspector";
- internal static StyleSheet GetInspectorStyleSheet()=>Resources.Load(InspectorStyleSheetPath);
+ internal static Color AkiBlue = new(140 / 255f, 160 / 255f, 250 / 255f);
+ internal static Color AkiRed = new(253 / 255f, 163 / 255f, 255 / 255f);
+ private const string InspectorStyleSheetPath = "AkiGOAP/Inspector";
+ internal static StyleSheet GetInspectorStyleSheet() => Resources.Load(InspectorStyleSheetPath);
}
public static class UIElementExtension
{
@@ -40,24 +40,24 @@ public static class UIElementExtension
///
///
///
- public static VisualElement AddSpace(this VisualElement parent,int height=10)
+ public static VisualElement AddSpace(this VisualElement parent, int height = 10)
{
- var space=new VisualElement();
- space.style.height=height;
+ var space = new VisualElement();
+ space.style.height = height;
space.AddTo(parent);
return parent;
}
- public static VisualElement AddTo(this VisualElement child,VisualElement parent)
+ public static VisualElement AddTo(this VisualElement child, VisualElement parent)
{
parent.Add(child);
return child;
}
- public static VisualElement Enabled(this VisualElement child,bool enabled)
+ public static VisualElement Enabled(this VisualElement child, bool enabled)
{
child.SetEnabled(enabled);
return child;
}
- public static VisualElement MoveToEnd(this VisualElement child,VisualElement parent)
+ public static VisualElement MoveToEnd(this VisualElement child, VisualElement parent)
{
parent.Remove(child);
parent.Add(child);
diff --git a/Editor/Window/GOAPEditorWindow.cs b/Editor/Window/GOAPEditorWindow.cs
index a17943b..7b054e5 100644
--- a/Editor/Window/GOAPEditorWindow.cs
+++ b/Editor/Window/GOAPEditorWindow.cs
@@ -12,17 +12,17 @@ public class GOAPEditorWindow : EditorWindow
private GOAPView graphView;
public static void ShowEditorWindow(IGOAPSet set)
{
- var key = set._Object.GetHashCode();
+ var key = set.Object.GetHashCode();
if (cache.ContainsKey(key))
{
cache[key].Focus();
return;
}
var window = CreateInstance();
- window.titleContent = new GUIContent($"GOAP Editor ({set._Object.name})");
+ window.titleContent = new GUIContent($"GOAP Editor ({set.Object.name})");
window.Show();
window.Focus();
- window.Key = set._Object;
+ window.Key = set.Object;
cache[key] = window;
window.StructGraphView(set);
}
diff --git a/Editor/Window/GOAPView.cs b/Editor/Window/GOAPView.cs
index cc2b481..9720a5e 100644
--- a/Editor/Window/GOAPView.cs
+++ b/Editor/Window/GOAPView.cs
@@ -161,7 +161,7 @@ internal void Save()
node.Commit();
set.Behaviors.Add(node.NodeBehavior);
}
- EditorUtility.SetDirty(set._Object);
+ EditorUtility.SetDirty(set.Object);
AssetDatabase.SaveAssets();
}
}
diff --git a/Example/Scene/AIExample.unity b/Example/Scene/AIExample.unity
index b749f39..6dd1320 100644
--- a/Example/Scene/AIExample.unity
+++ b/Example/Scene/AIExample.unity
@@ -123,6 +123,103 @@ NavMeshSettings:
debug:
m_Flags: 0
m_NavMeshData: {fileID: 23800000, guid: 9249c4e651a415041b2f9054245dd370, type: 2}
+--- !u!1 &177913116
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 177913117}
+ - component: {fileID: 177913119}
+ - component: {fileID: 177913118}
+ m_Layer: 0
+ m_Name: Info
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &177913117
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 177913116}
+ m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
+ m_LocalPosition: {x: 0, y: 1, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 1643126468}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
+--- !u!102 &177913118
+TextMesh:
+ serializedVersion: 3
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 177913116}
+ m_Text: Tent
+ m_OffsetZ: 0
+ m_CharacterSize: 1
+ m_LineSpacing: 1
+ m_Anchor: 0
+ m_Alignment: 0
+ m_TabSize: 4
+ m_FontSize: 15
+ m_FontStyle: 0
+ m_RichText: 1
+ m_Font: {fileID: 0}
+ m_Color:
+ serializedVersion: 2
+ rgba: 4278255438
+--- !u!23 &177913119
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 177913116}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &488310074
GameObject:
m_ObjectHideFlags: 0
@@ -329,10 +426,11 @@ Transform:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 754640373}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: -8.41, y: 1, z: 2.85}
+ m_LocalPosition: {x: -5.12, y: 1, z: 2.85}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
- m_Children: []
+ m_Children:
+ - {fileID: 1652185201}
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -412,13 +510,369 @@ Transform:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 854926615}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: 1.91, y: 0.93, z: -8.12}
+ m_LocalPosition: {x: 2.71, y: 0.93, z: -7.7}
m_LocalScale: {x: 2, y: 2, z: 2}
m_ConstrainProportionsScale: 0
- m_Children: []
+ m_Children:
+ - {fileID: 1042871445}
m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1001935118
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1001935121}
+ - component: {fileID: 1001935120}
+ - component: {fileID: 1001935119}
+ m_Layer: 0
+ m_Name: EventSystem
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &1001935119
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1001935118}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_SendPointerHoverToParent: 1
+ m_HorizontalAxis: Horizontal
+ m_VerticalAxis: Vertical
+ m_SubmitButton: Submit
+ m_CancelButton: Cancel
+ m_InputActionsPerSecond: 10
+ m_RepeatDelay: 0.5
+ m_ForceModuleActive: 0
+--- !u!114 &1001935120
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1001935118}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_FirstSelected: {fileID: 0}
+ m_sendNavigationEvents: 1
+ m_DragThreshold: 10
+--- !u!4 &1001935121
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1001935118}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 6
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1042871444
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1042871445}
+ - component: {fileID: 1042871447}
+ - component: {fileID: 1042871446}
+ m_Layer: 0
+ m_Name: Info
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1042871445
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1042871444}
+ m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
+ m_LocalPosition: {x: 0, y: 1, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 854926618}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
+--- !u!102 &1042871446
+TextMesh:
+ serializedVersion: 3
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1042871444}
+ m_Text: Home
+ m_OffsetZ: 0
+ m_CharacterSize: 1
+ m_LineSpacing: 1
+ m_Anchor: 0
+ m_Alignment: 0
+ m_TabSize: 4
+ m_FontSize: 15
+ m_FontStyle: 0
+ m_RichText: 1
+ m_Font: {fileID: 0}
+ m_Color:
+ serializedVersion: 2
+ rgba: 4278255438
+--- !u!23 &1042871447
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1042871444}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!1 &1389330038
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1389330039}
+ - component: {fileID: 1389330041}
+ - component: {fileID: 1389330040}
+ m_Layer: 5
+ m_Name: Text (Legacy)
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &1389330039
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1389330038}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 1398099457}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: -515.0315, y: 262.61505}
+ m_SizeDelta: {x: -1030.063, y: -525.2301}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1389330040
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1389330038}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 1
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+ m_Maskable: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 45
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 4
+ m_MaxSize: 45
+ m_Alignment: 0
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: '1. After clicking to start the game, a specified number of Agents will
+ be generated in the scene, and they will first enter the Goal
+ 1 to follow the player and gradually consume energy
+
+ 2. Enter Goal
+ 2 after the energy is exhausted, move to Home or Tent, recover full energy
+ after a period of rest, and re-enter Goal 1.
+
+ See
+ REAMD.md for detail'
+--- !u!222 &1389330041
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1389330038}
+ m_CullTransparentMesh: 1
+--- !u!1 &1398099453
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1398099457}
+ - component: {fileID: 1398099456}
+ - component: {fileID: 1398099455}
+ - component: {fileID: 1398099454}
+ m_Layer: 5
+ m_Name: Canvas
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &1398099454
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1398099453}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreReversedGraphics: 1
+ m_BlockingObjects: 0
+ m_BlockingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+--- !u!114 &1398099455
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1398099453}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_UiScaleMode: 0
+ m_ReferencePixelsPerUnit: 100
+ m_ScaleFactor: 1
+ m_ReferenceResolution: {x: 800, y: 600}
+ m_ScreenMatchMode: 0
+ m_MatchWidthOrHeight: 0
+ m_PhysicalUnit: 3
+ m_FallbackScreenDPI: 96
+ m_DefaultSpriteDPI: 96
+ m_DynamicPixelsPerUnit: 1
+ m_PresetInfoIsWorld: 0
+--- !u!223 &1398099456
+Canvas:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1398099453}
+ m_Enabled: 1
+ serializedVersion: 3
+ m_RenderMode: 0
+ m_Camera: {fileID: 0}
+ m_PlaneDistance: 100
+ m_PixelPerfect: 0
+ m_ReceivesEvents: 1
+ m_OverrideSorting: 0
+ m_OverridePixelPerfect: 0
+ m_SortingBucketNormalizedSize: 0
+ m_AdditionalShaderChannelsFlag: 0
+ m_SortingLayerID: 0
+ m_SortingOrder: 0
+ m_TargetDisplay: 0
+--- !u!224 &1398099457
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1398099453}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0, y: 0, z: 0}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 1389330039}
+ m_Father: {fileID: 0}
+ m_RootOrder: 5
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0, y: 0}
--- !u!1 &1597734653
GameObject:
m_ObjectHideFlags: 0
@@ -469,7 +923,6 @@ MonoBehaviour:
home: {fileID: 854926618}
tent: {fileID: 1643126468}
dataSet: {fileID: 11400000, guid: fb386ac86c5b19e4e83bfa651d446200, type: 2}
- agents: []
--- !u!1 &1643126465
GameObject:
m_ObjectHideFlags: 0
@@ -546,13 +999,111 @@ Transform:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1643126465}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: -11.66, y: 0.93, z: -8.12}
+ m_LocalPosition: {x: -12.34, y: 0.93, z: -7.69}
m_LocalScale: {x: 2, y: 2, z: 2}
m_ConstrainProportionsScale: 0
- m_Children: []
+ m_Children:
+ - {fileID: 177913117}
m_Father: {fileID: 0}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1652185200
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1652185201}
+ - component: {fileID: 1652185203}
+ - component: {fileID: 1652185202}
+ m_Layer: 0
+ m_Name: Info
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1652185201
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1652185200}
+ m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
+ m_LocalPosition: {x: 0, y: 1, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 754640377}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
+--- !u!102 &1652185202
+TextMesh:
+ serializedVersion: 3
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1652185200}
+ m_Text: Player
+ m_OffsetZ: 0
+ m_CharacterSize: 1
+ m_LineSpacing: 1
+ m_Anchor: 0
+ m_Alignment: 0
+ m_TabSize: 4
+ m_FontSize: 15
+ m_FontStyle: 0
+ m_RichText: 1
+ m_Font: {fileID: 0}
+ m_Color:
+ serializedVersion: 2
+ rgba: 4293263104
+--- !u!23 &1652185203
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1652185200}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &1953241685
GameObject:
m_ObjectHideFlags: 0
@@ -762,14 +1313,14 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2130379000}
- m_LocalRotation: {x: 0.5150381, y: 0, z: 0, w: 0.85716736}
- m_LocalPosition: {x: -1.3, y: 22.5, z: -20.13}
+ m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
+ m_LocalPosition: {x: -3.96, y: 27.5, z: -9.68}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1953241686}
m_RootOrder: 0
- m_LocalEulerAnglesHint: {x: 62, y: 0, z: 0}
+ m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
--- !u!114 &2130379004
MonoBehaviour:
m_ObjectHideFlags: 0
diff --git a/Example/Scripts/Action/GoToHome.cs b/Example/Scripts/Action/GoToHome.cs
index e0f5c08..3209ffb 100644
--- a/Example/Scripts/Action/GoToHome.cs
+++ b/Example/Scripts/Action/GoToHome.cs
@@ -7,12 +7,12 @@ public class GoToHome : ExampleAction
protected sealed override void SetupDerived()
{
//Set precondition to let action automatically cancel
- preconditions["CanRest"]=false;
- worldState.RegisterNodeTarget(this,agent.Home);
+ Preconditions["CanRest"] = false;
+ worldState.RegisterNodeTarget(this, agent.Home);
}
protected sealed override void SetupEffects()
{
- effects["CanRest"]=true;
+ Effects["CanRest"] = true;
}
public sealed override float GetCost()
{
@@ -22,7 +22,7 @@ public sealed override void OnTick()
{
agent.NavMeshAgent.SetDestination(agent.Home.position);
//You can make a trigger to set state or other method based on unity engine lifetime scope
- worldState.SetState("CanRest",Vector3.SqrMagnitude(agent._Transform.position-agent.Home.position)<1);
+ worldState.SetState("CanRest", Vector3.SqrMagnitude(agent._Transform.position - agent.Home.position) < 1);
}
}
}
diff --git a/Example/Scripts/Action/GoToPlayer.cs b/Example/Scripts/Action/GoToPlayer.cs
index cd1783a..e9b4b66 100644
--- a/Example/Scripts/Action/GoToPlayer.cs
+++ b/Example/Scripts/Action/GoToPlayer.cs
@@ -5,7 +5,7 @@ public class GoToPlayer : ExampleAction
{
protected sealed override void SetupEffects()
{
- effects["InDistance"]=true;
+ Effects["InDistance"] = true;
}
public sealed override float GetCost()
{
diff --git a/Example/Scripts/Action/GoToTent.cs b/Example/Scripts/Action/GoToTent.cs
index 0f11f0d..1d69f92 100644
--- a/Example/Scripts/Action/GoToTent.cs
+++ b/Example/Scripts/Action/GoToTent.cs
@@ -7,12 +7,12 @@ public class GoToTent : ExampleAction
protected sealed override void SetupDerived()
{
//Set precondition to let action automatically cancel
- preconditions["CanRest"]=false;
- worldState.RegisterNodeTarget(this,agent.Tent);
+ Preconditions["CanRest"] = false;
+ worldState.RegisterNodeTarget(this, agent.Tent);
}
protected sealed override void SetupEffects()
{
- effects["CanRest"]=true;
+ Effects["CanRest"] = true;
}
public sealed override float GetCost()
{
@@ -22,7 +22,7 @@ public sealed override void OnTick()
{
agent.NavMeshAgent.SetDestination(agent.Tent.position);
//You can make a trigger to set state or other method based on unity engine lifetime scope
- worldState.SetState("CanRest",Vector3.SqrMagnitude(agent._Transform.position-agent.Tent.position)<1);
+ worldState.SetState("CanRest", Vector3.SqrMagnitude(agent._Transform.position - agent.Tent.position) < 1);
}
}
}
diff --git a/Example/Scripts/Action/HaveARest.cs b/Example/Scripts/Action/HaveARest.cs
index 06eef40..c2766c9 100644
--- a/Example/Scripts/Action/HaveARest.cs
+++ b/Example/Scripts/Action/HaveARest.cs
@@ -6,38 +6,38 @@ public class HaveARest : ExampleAction
{
//We can expose some property to the graph editor
//Use GOAPLabel to change the label of field in graph editor
- [SerializeField,GOAPLabel("Wait Time 等待时间")]
- private float waitTime=5;
+ [SerializeField, GOAPLabel("Wait Time 等待时间")]
+ private float waitTime = 5;
private float timer;
protected sealed override void SetupDerived()
{
- preconditions["CanRest"]=true;
+ Preconditions["CanRest"] = true;
}
protected sealed override void SetupEffects()
{
- effects["HaveEnergy"]=true;
+ Effects["HaveEnergy"] = true;
}
public sealed override void OnTick()
{
- timer+=Time.deltaTime;
- if(timer>=waitTime)
+ timer += Time.deltaTime;
+ if (timer >= waitTime)
{
- timer=0;
- agent.Energy=100;
- worldState.SetState("HaveEnergy",true);
+ timer = 0;
+ agent.Energy = 100;
+ worldState.SetState("HaveEnergy", true);
}
}
protected sealed override void OnActivateDerived()
{
//Reset timer when enter this action
- timer=0;
- agent.NavMeshAgent.isStopped=true;
+ timer = 0;
+ agent.NavMeshAgent.isStopped = true;
}
protected sealed override void OnDeactivateDerived()
{
- agent.NavMeshAgent.isStopped=false;
+ agent.NavMeshAgent.isStopped = false;
//Reset signal state
- worldState.SetState("CanRest",false);
+ worldState.SetState("CanRest", false);
}
}
}
diff --git a/Example/Scripts/Action/Idle.cs b/Example/Scripts/Action/Idle.cs
index 1c46745..c05000a 100644
--- a/Example/Scripts/Action/Idle.cs
+++ b/Example/Scripts/Action/Idle.cs
@@ -5,8 +5,8 @@ public class Idle : ExampleAction
{
protected sealed override void SetupDerived()
{
- preconditions["InDistance"]=true;
- preconditions["HaveEnergy"]=true;
+ Preconditions["InDistance"] = true;
+ Preconditions["HaveEnergy"] = true;
}
public sealed override float GetCost()
{
diff --git a/Example/Scripts/Goal/ExampleGoal.cs b/Example/Scripts/Goal/ExampleGoal.cs
index ef0043d..498f64e 100644
--- a/Example/Scripts/Goal/ExampleGoal.cs
+++ b/Example/Scripts/Goal/ExampleGoal.cs
@@ -5,7 +5,7 @@ public abstract class ExampleGoal : GOAPGoal
protected ExampleAgent agent;
public void Inject(ExampleAgent agent)
{
- this.agent=agent;
+ this.agent = agent;
}
}
}
diff --git a/Example/Scripts/Goal/FollowPlayerGoal.cs b/Example/Scripts/Goal/FollowPlayerGoal.cs
index 328a1ca..9d9e623 100644
--- a/Example/Scripts/Goal/FollowPlayerGoal.cs
+++ b/Example/Scripts/Goal/FollowPlayerGoal.cs
@@ -5,19 +5,21 @@ namespace Kurisu.GOAP.Example
public class FollowPlayerGoal : ExampleGoal
{
[SerializeField]
- private float distance=4;
- protected sealed override void SetupDerived(){
- preconditions["HaveEnergy"]=true;
- preconditions["InDistance"]=false;
- conditions["InDistance"]=true;
+ private float distance = 4;
+ protected sealed override void SetupDerived()
+ {
+ Preconditions["HaveEnergy"] = true;
+ Preconditions["InDistance"] = false;
+ Conditions["InDistance"] = true;
}
- public sealed override float GetPriority(){
+ public sealed override float GetPriority()
+ {
return 1f;
}
public sealed override void OnTick()
{
//If distance * distance is smaller than 4, set 'InDistance' to true
- worldState.SetState("InDistance",Vector3.SqrMagnitude(agent._Transform.position-agent.Player.position)minAmount);
+ worldState.SetState("HaveEnergy", agent.Energy > minAmount);
}
}
}
diff --git a/Runtime/AssemblyInfo.cs b/Runtime/AssemblyInfo.cs
new file mode 100644
index 0000000..cd23f84
--- /dev/null
+++ b/Runtime/AssemblyInfo.cs
@@ -0,0 +1,2 @@
+using System.Runtime.CompilerServices;
+[assembly: InternalsVisibleTo("Kurisu.GOAP.Editor")]
\ No newline at end of file
diff --git a/Runtime/AssemblyInfo.cs.meta b/Runtime/AssemblyInfo.cs.meta
new file mode 100644
index 0000000..7f53cb1
--- /dev/null
+++ b/Runtime/AssemblyInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 315523a2125eb3e429fc32c72f0ac0c9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Component/GOAPPlanner.cs b/Runtime/Component/GOAPPlanner.cs
index 7cb8786..f5db3e5 100644
--- a/Runtime/Component/GOAPPlanner.cs
+++ b/Runtime/Component/GOAPPlanner.cs
@@ -10,12 +10,14 @@ namespace Kurisu.GOAP
///
/// Used to package GOAPGoal data for other classes (e.g. PlannerSnapshot)
///
- public struct GoalData{
+ public struct GoalData
+ {
public string goalName;
public float priority;
public bool canRun;
- public GoalData(string goalName, float priority, bool canRun){
+ public GoalData(string goalName, float priority, bool canRun)
+ {
this.priority = priority;
this.goalName = goalName;
this.canRun = canRun;
@@ -24,11 +26,13 @@ public GoalData(string goalName, float priority, bool canRun){
///
/// Used to create a linked list of GOAPActions
///
- public class ActionNode{
+ public class ActionNode
+ {
public ActionNode parent;
public IAction action;
- public ActionNode(ActionNode parent, IAction action){
+ public ActionNode(ActionNode parent, IAction action)
+ {
this.parent = parent;
this.action = action;
}
@@ -36,15 +40,15 @@ public ActionNode(ActionNode parent, IAction action){
[Flags]
internal enum LogType
{
- OnlyActive=2,
- IncludeSearch=4,
- IncludeFail=8
+ OnlyActive = 2,
+ IncludeSearch = 4,
+ IncludeFail = 8
}
[Flags]
internal enum TickType
{
- ManualUpdateGoal=2,
- ManualActivatePlanner=4
+ ManualUpdateGoal = 2,
+ ManualActivatePlanner = 4
}
///
/// class GOAPPlanner
@@ -52,54 +56,56 @@ internal enum TickType
/// and carries out that action plan.
///
[RequireComponent(typeof(GOAPWorldState))]
- public class GOAPPlanner:MonoBehaviour,IPlanner
- {
+ public class GOAPPlanner : MonoBehaviour, IPlanner
+ {
protected GOAPWorldState worldState;
- public GOAPWorldState WorldState=>worldState;
- protected readonly List goals=new();
- protected readonly List actions=new();
+ public GOAPWorldState WorldState => worldState;
+ protected readonly List goals = new();
+ protected readonly List actions = new();
//// Active
- public IGoal ActivateGoal{get; private set;}
- public int activeActionIndex{get; private set;}
- public List ActivatePlan{get; private set;}
+ public IGoal ActivateGoal { get; private set; }
+ public int ActiveActionIndex { get; private set; }
+ public List ActivatePlan { get; private set; }
//// Optimal
private IGoal optimalGoal;
private List optimalPlan;
// 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);
- public List Behaviors=>Enumerable.Empty().Concat(actions.OfType()).Concat(goals.OfType()).ToList();
- public UnityEngine.Object _Object =>gameObject;
+ private bool LogActive => logType.HasFlag(LogType.OnlyActive);
+ private bool LogSearch => logType.HasFlag(LogType.IncludeSearch);
+ private bool LogFail => logType.HasFlag(LogType.IncludeFail);
+ public List Behaviors => Enumerable.Empty().Concat(actions.OfType()).Concat(goals.OfType()).ToList();
+ public UnityEngine.Object Object => gameObject;
public event System.Action OnUpdatePlanEvent;
- [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,"+
+ [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;
- private Queue> poolQueue = new Queue>();
- public bool IsActive{get;private set;}=true;
- private void Awake() {
+ private readonly Queue> poolQueue = new();
+ public bool IsActive { get; private set; } = true;
+ private void Awake()
+ {
worldState = GetComponent();
- IsActive&=!tickType.HasFlag(TickType.ManualActivatePlanner);
+ IsActive &= !tickType.HasFlag(TickType.ManualActivatePlanner);
}
- private void Update(){
+ private void Update()
+ {
//If has ActivePlan and mode is manualStart
- if(IsActive)Tick();
+ if (IsActive) Tick();
}
public void ManualActivate()
{
- IsActive=true;
+ IsActive = true;
}
public void InjectGoals(IEnumerable source)
{
goals.Clear();
- foreach(var goal in source)
+ foreach (var goal in source)
{
goals.Add(goal);
goal.Init(worldState);
@@ -108,130 +114,149 @@ public void InjectGoals(IEnumerable source)
public void InjectActions(IEnumerable source)
{
actions.Clear();
- foreach(var action in source)
+ foreach (var action in source)
{
actions.Add(action);
action.Init(worldState);
}
}
- List IPlanner.GetAllActions()=>actions;
+ List IPlanner.GetAllActions() => actions;
public void Tick()
{
- if(!tickType.HasFlag(TickType.ManualUpdateGoal))TickGoals();
+ if (!tickType.HasFlag(TickType.ManualUpdateGoal)) TickGoals();
OnTickActivePlan();
- GetHighestPriorityGoal(chosenGoal:out optimalGoal,out optimalPlan);
- if ((NoActiveGoal() && GoalAvailable()) || BetterGoalAvailable()){
+ GetHighestPriorityGoal(chosenGoal: out optimalGoal, out optimalPlan);
+ if ((NoActiveGoal() && GoalAvailable()) || BetterGoalAvailable())
+ {
StartCurrentBestGoal();
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 List GetPlan()
{
- if(poolQueue.Count!=0)
+ if (poolQueue.Count != 0)
{
- var plan=poolQueue.Dequeue();
+ var plan = poolQueue.Dequeue();
plan.Clear();
return plan;
}
return new List();
}
- private bool NoActiveGoal(){
+ private bool NoActiveGoal()
+ {
return ActivateGoal == null;
}
- private bool BetterGoalAvailable(){
+ private bool BetterGoalAvailable()
+ {
return optimalGoal != null && optimalGoal != ActivateGoal;
}
- private bool GoalAvailable(){
+ private bool GoalAvailable()
+ {
return optimalPlan != null && optimalGoal != null;
}
- private void StartCurrentBestGoal(){
- if (ActivateGoal != null){
+ private void StartCurrentBestGoal()
+ {
+ if (ActivateGoal != null)
+ {
ActivateGoal.OnDeactivate();
}
- if (ActivatePlan != null && activeActionIndex < ActivatePlan.Count){
- ActivatePlan[activeActionIndex].OnDeactivate();
+ if (ActivatePlan != null && ActiveActionIndex < ActivatePlan.Count)
+ {
+ ActivatePlan[ActiveActionIndex].OnDeactivate();
}
- activeActionIndex = 0;
+ ActiveActionIndex = 0;
ActivateGoal = optimalGoal;
- if(ActivatePlan!=null)poolQueue.Enqueue(ActivatePlan);
+ if (ActivatePlan != null) poolQueue.Enqueue(ActivatePlan);
ActivatePlan = optimalPlan;
- if(logActive)ActivePlanLog($"Starting new plan for {ActivateGoal.Name}", bold:true);
+ if (LogActive) ActivePlanLog($"Starting new plan for {ActivateGoal.Name}", bold: true);
ActivateGoal.OnActivate();
- if(logActive)ActivePlanLog($"Starting {ActivatePlan[activeActionIndex].Name}");
- ActivatePlan[activeActionIndex].OnActivate();
+ if (LogActive) ActivePlanLog($"Starting {ActivatePlan[ActiveActionIndex].Name}");
+ ActivatePlan[ActiveActionIndex].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 || ActivatePlan == null){ return; }
+ if (ActivateGoal == null || ActivatePlan == 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 (!(ActivatePlan[activeActionIndex].PreconditionsSatisfied(worldState))){
- if(logActive)ActivePlanLog(
- $"{ActivatePlan[activeActionIndex].Name} failed as preconditions are no longer satisfied",
- bold:true
+ if (!(ActivatePlan[ActiveActionIndex].PreconditionsSatisfied(worldState)))
+ {
+ if (LogActive) ActivePlanLog(
+ $"{ActivatePlan[ActiveActionIndex].Name} failed as preconditions are no longer satisfied",
+ bold: true
);
- OnCompleteOrFailActivePlan();
+ OnCompleteOrFailActivePlan();
return;
}
- ActivatePlan[activeActionIndex].OnTick();
+ ActivatePlan[ActiveActionIndex].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;
}
- if (activeActionIndex < ActivatePlan.Count-1){
+ if (ActiveActionIndex < ActivatePlan.Count - 1)
+ {
// At least one more action after activeAction
- for (int i=ActivatePlan.Count-1; i > activeActionIndex; i--){
- if (ActivatePlan[i].PreconditionsSatisfied(worldState)){
+ for (int i = ActivatePlan.Count - 1; i > ActiveActionIndex; i--)
+ {
+ if (ActivatePlan[i].PreconditionsSatisfied(worldState))
+ {
// Can skip to a new action
- if(logActive)ActivePlanLog($"Stopping {ActivatePlan[activeActionIndex].Name}");
- ActivatePlan[activeActionIndex].OnDeactivate();
- activeActionIndex = i;
- if(logActive)ActivePlanLog($"Moving to new action: {ActivatePlan[activeActionIndex].Name}");
- ActivatePlan[activeActionIndex].OnActivate();
+ if (LogActive) ActivePlanLog($"Stopping {ActivatePlan[ActiveActionIndex].Name}");
+ ActivatePlan[ActiveActionIndex].OnDeactivate();
+ ActiveActionIndex = i;
+ if (LogActive) ActivePlanLog($"Moving to new action: {ActivatePlan[ActiveActionIndex].Name}");
+ ActivatePlan[ActiveActionIndex].OnActivate();
}
}
}
}
- private void OnCompleteOrFailActivePlan(){
- if (ActivatePlan != null){
- ActivatePlan[activeActionIndex].OnDeactivate();
+ private void OnCompleteOrFailActivePlan()
+ {
+ if (ActivatePlan != null)
+ {
+ ActivatePlan[ActiveActionIndex].OnDeactivate();
}
ActivateGoal?.OnDeactivate();
ActivateGoal = null;
@@ -245,50 +270,59 @@ private void OnCompleteOrFailActivePlan(){
///
///
///
- private void GetHighestPriorityGoal(out IGoal chosenGoal,out List chosenPlan){
+ private void GetHighestPriorityGoal(out IGoal chosenGoal, out List chosenPlan)
+ {
chosenGoal = null;
chosenPlan = null;
//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;
}
- if (chosenGoal != null &&!HasHigherPriority(goals[i], chosenGoal)){
+ if (chosenGoal != null && !HasHigherPriority(goals[i], chosenGoal))
+ {
continue;
}
- if(chosenGoal!=null&&logSearch)
+ if (chosenGoal != null && LogSearch)
PlannerLog($"{goals[i].Name} has higher priority than {chosenGoal.Name}");
List candidatePath = GetOptimalPath(
- currentState:worldState,
- goal:goals[i]
+ currentState: worldState,
+ goal: goals[i]
);
- if (candidatePath != null){
+ if (candidatePath != null)
+ {
chosenGoal = goals[i];
//Use ObjectPool to recycle Plan List
- chosenPlan=GetPlan();
+ chosenPlan = GetPlan();
chosenPlan.AddRange(candidatePath);
- if(logSearch)PlannerLog($"Path found. Chosen goal is now {goals[i].Name}", bold:true);
+ if (LogSearch) PlannerLog($"Path found. Chosen goal is now {goals[i].Name}", bold: true);
}
}
}
- private bool HasHigherPriority(IGoal goal, IGoal other){
+ private bool HasHigherPriority(IGoal goal, IGoal other)
+ {
return goal.GetPriority() > other.GetPriority();
}
private static bool InList(IAction action, List nodeList)
{
- for(int i = 0; i < nodeList.Count; i++){
- if (nodeList[i].action == action){
+ for (int i = 0; i < nodeList.Count; i++)
+ {
+ if (nodeList[i].action == action)
+ {
return true;
}
}
@@ -297,49 +331,52 @@ private static bool InList(IAction action, List nodeList)
#region List Cache
private List openListCache = new List();
private List closedListCache = new List();
- private List validStartActionsCache=new List();
- private List linkNodesCache=new List();
- private List candicatePath=new List();
+ private List validStartActionsCache = new List();
+ private List linkNodesCache = new List();
+ private List candicatePath = new List();
#endregion
//// A*
private List GetOptimalPath(GOAPWorldState currentState, IGoal goal)
{
- if(logSearch)PlannerLog($"Searching for plan for {goal.Name}", bold:true);
+ if (LogSearch) PlannerLog($"Searching for plan for {goal.Name}", bold: true);
validStartActionsCache.Clear();
- for (int i = 0; i< actions.Count; i++){
- if (actions[i].SatisfiesConditions(goal.conditions)){
+ for (int i = 0; i < actions.Count; i++)
+ {
+ if (actions[i].SatisfiesConditions(goal.Conditions))
+ {
validStartActionsCache.Add(actions[i]);
- if(logSearch)PlannerLog($"{actions[i].Name} satisfies goal conditions");
+ if (LogSearch) PlannerLog($"{actions[i].Name} satisfies goal conditions");
}
}
// No path found
- if (validStartActionsCache.Count == 0){
- if(logSearch)PlannerLog($"No actions found to satisfy {goal.Name} conditions");
+ if (validStartActionsCache.Count == 0)
+ {
+ if (LogSearch) PlannerLog($"No actions found to satisfy {goal.Name} conditions");
return null;
}
validStartActionsCache.Sort(CompareCost);
- List path=null;
- for(int i=0;i path = null;
+ for (int i = 0; i < validStartActionsCache.Count; i++)
{
openListCache.Clear();
closedListCache.Clear();
- IAction startAction=validStartActionsCache[i];
+ IAction startAction = validStartActionsCache[i];
openListCache.Add(new ActionNode(null, startAction));
- if(logSearch)PlannerLog($"Selected {startAction.Name}, cost:{startAction.GetCost()}", bold:true);
- path=SearchPath(worldState,openListCache,closedListCache,goal);
- if(path!=null)return path;
+ if (LogSearch) PlannerLog($"Selected {startAction.Name}, cost:{startAction.GetCost()}", bold: true);
+ path = SearchPath(worldState, openListCache, closedListCache, goal);
+ if (path != null) return path;
//Since we don't know the start node is valid,we need to fall back to the next start action
- if(logSearch)PlannerLog($"Path fall back time:{i}", bold:true);
+ if (LogSearch) PlannerLog($"Path fall back time:{i}", bold: true);
}
- if(logFail)PlannerLog("No path found.");
- return null;
+ if (LogFail) PlannerLog("No path found.");
+ return null;
}
- private static int CompareCost(IAction a,IAction b)
+ private static int CompareCost(IAction a, IAction b)
{
- float costA=a.GetCost();
- float costB=b.GetCost();
- if(costAcostB)return 1;
+ float costA = a.GetCost();
+ float costB = b.GetCost();
+ if (costA < costB) return -1;
+ else if (costA > costB) return 1;
else return 0;
}
///
@@ -353,49 +390,56 @@ private static int CompareCost(IAction a,IAction b)
private List SearchPath(
GOAPWorldState currentState,
List openList,
- List closedList,
+ List closedList,
IGoal goal)
{
-
- while (openList.Count != 0){
+
+ while (openList.Count != 0)
+ {
ActionNode currentNode = null;
- if (closedList.Count>0){
+ if (closedList.Count > 0)
+ {
currentNode = GetNextNode(
- closedList:closedList,
- openList:openList
+ closedList: closedList,
+ openList: openList
);
}
- else{
- float nodeCost;
+ else
+ {
currentNode = GetNextNode(
- requiredState:goal.conditions,
- openList:openList,
- nodeCost: out nodeCost,
- isStartNode:true
+ requiredState: goal.Conditions,
+ openList: openList,
+ nodeCost: out float nodeCost,
+ isStartNode: true
);
}
- if (currentNode == null){
- if(logFail)PlannerLog("No path found");
+ if (currentNode == null)
+ {
+ if (LogFail) PlannerLog("No path found");
return null;
}
//找到的代价最小且满足前一结点条件的加入CloseList
- closedList.Add(currentNode);
+ closedList.Add(currentNode);
openList.Remove(currentNode);
//If CurrentNode can satisfy current state(world State)
//如果已经满足当前条件则直接返回
- if (currentState.IsSubset(currentNode.action.preconditions)){
+ if (currentState.IsSubset(currentNode.action.Preconditions))
+ {
return GeneratePath(closedList);
}
//将满足该结点要求且相邻结点加入OpenList
List linkedActions = GetLinkedActions(
- node:currentNode.action
+ node: currentNode.action
);
- for (int i = 0; i < linkedActions.Count; i++){
- if (!InList(linkedActions[i], closedList)){
- if (!InList(linkedActions[i], openList)){
+ for (int i = 0; i < linkedActions.Count; i++)
+ {
+ if (!InList(linkedActions[i], closedList))
+ {
+ if (!InList(linkedActions[i], openList))
+ {
openList.Add(new ActionNode(currentNode, linkedActions[i]));
}
}
@@ -413,13 +457,14 @@ private List GeneratePath(List closedList)
{
candicatePath.Clear();
ActionNode currentNode = closedList[closedList.Count - 1];
- while(currentNode.parent != null){
+ while (currentNode.parent != null)
+ {
candicatePath.Add(currentNode.action);
currentNode = currentNode.parent;
}
candicatePath.Add(currentNode.action);
return candicatePath;
- }
+ }
///
/// Finds the ActionNode in openList that satisfies
/// conditions of an ActionNode in closedList with the lowest cost.
@@ -430,25 +475,29 @@ private List GeneratePath(List closedList)
private ActionNode GetNextNode(List closedList, List openList)
{
float minCost = -1f;
- ActionNode nextNode=null;
+ ActionNode nextNode = null;
ActionNode currentNode;
- for (int i = 0; i < closedList.Count; i++){
+ for (int i = 0; i < closedList.Count; i++)
+ {
float nodeCost;
currentNode = GetNextNode(
- closedList[i].action.preconditions,
+ closedList[i].action.Preconditions,
openList,
out nodeCost
);
- if ((minCost < 0 || nodeCost < minCost) && currentNode != null){
+ if ((minCost < 0 || nodeCost < minCost) && currentNode != null)
+ {
nextNode = currentNode;
minCost = nodeCost;
}
}
- if (nextNode!=null){
- if(logSearch)PlannerLog($"Selected {nextNode.action.Name}", bold:true);
+ if (nextNode != null)
+ {
+ if (LogSearch) PlannerLog($"Selected {nextNode.action.Name}", bold: true);
}
- else{
- if(logFail)PlannerLog("Could not find next action");
+ else
+ {
+ if (LogFail) PlannerLog("Could not find next action");
}
return nextNode;
}
@@ -461,27 +510,32 @@ out nodeCost
///
///
///
- private ActionNode GetNextNode(Dictionary requiredState, List openList,out float nodeCost,bool isStartNode=false){
- float minCost = -1f;
- ActionNode nextNode = null;
- for (int i = 0; i < openList.Count; i++){
- if (!openList[i].action.SatisfiesConditions(requiredState)){
- if(logFail)PlannerLog($"{openList[i].action.Name} does not satisfy conditions");
- continue;
- }
- if (!isStartNode){
- //开始结点肯定满足要求(因为已经进行过condition检测)
- if(logSearch)PlannerLog($"{openList[i].action.Name} satisfies conditions");
- }
- float cost = openList[i].action.GetCost();
- if (minCost < 0 || cost < minCost){
- nextNode = openList[i];
- minCost = cost;
- }
+ private ActionNode GetNextNode(Dictionary requiredState, List openList, out float nodeCost, bool isStartNode = false)
+ {
+ float minCost = -1f;
+ ActionNode nextNode = null;
+ for (int i = 0; i < openList.Count; i++)
+ {
+ if (!openList[i].action.SatisfiesConditions(requiredState))
+ {
+ if (LogFail) PlannerLog($"{openList[i].action.Name} does not satisfy conditions");
+ continue;
+ }
+ if (!isStartNode)
+ {
+ //开始结点肯定满足要求(因为已经进行过condition检测)
+ if (LogSearch) PlannerLog($"{openList[i].action.Name} satisfies conditions");
+ }
+ float cost = openList[i].action.GetCost();
+ if (minCost < 0 || cost < minCost)
+ {
+ nextNode = openList[i];
+ minCost = cost;
}
- nodeCost = minCost;
- return nextNode;
}
+ nodeCost = minCost;
+ return nextNode;
+ }
///
/// Searches availableNodes for all those that satisfy node.preconditions
/// and that are not in path.
@@ -490,11 +544,13 @@ private ActionNode GetNextNode(Dictionary requiredState, List
private List GetLinkedActions(IAction node)
{
- if(logSearch)PlannerLog($"Finding actions linked to {node.Name}");
+ if (LogSearch) PlannerLog($"Finding actions linked to {node.Name}");
linkNodesCache.Clear();
- for (int i = 0; i < actions.Count; i++){
- if (actions[i].SatisfiesConditions(node.preconditions)){
- if(logSearch)PlannerLog($"{actions[i].Name} is linked to {node.Name}");
+ for (int i = 0; i < actions.Count; i++)
+ {
+ if (actions[i].SatisfiesConditions(node.Preconditions))
+ {
+ if (LogSearch) PlannerLog($"{actions[i].Name} is linked to {node.Name}");
linkNodesCache.Add(actions[i]);
}
}
@@ -506,8 +562,9 @@ private List GetLinkedActions(IAction node)
///
List IPlanner.GetSortedGoalData()
{
- List goalData = new List();
- for (int i=0; i goalData = new();
+ for (int i = 0; i < goals.Count; i++)
+ {
goalData.Add(
new GoalData(goals[i].Name, goals[i].GetPriority(), goals[i].PreconditionsSatisfied(worldState))
);
@@ -517,20 +574,24 @@ List IPlanner.GetSortedGoalData()
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/Component/GOAPPlannerPro.cs b/Runtime/Component/GOAPPlannerPro.cs
index fae673a..fca5c4e 100644
--- a/Runtime/Component/GOAPPlannerPro.cs
+++ b/Runtime/Component/GOAPPlannerPro.cs
@@ -16,7 +16,7 @@ public class GOAPPlannerPro : MonoBehaviour, IPlanner
[BurstCompile]
private struct GoalSorter : IComparer
{
- public int Compare(IGoal x, IGoal y)
+ public readonly int Compare(IGoal x, IGoal y)
{
return y.GetPriority().CompareTo(x.GetPriority());
}
@@ -26,23 +26,23 @@ public int Compare(IGoal x, IGoal y)
protected readonly List goals = new();
protected readonly List actions = new();
public IGoal ActivateGoal { get; private set; }
- int IPlanner.activeActionIndex => 0;
+ int IPlanner.ActiveActionIndex => 0;
private IAction candidateAction;
private IAction activateAction;
- private List candidatePlan = new();
+ private readonly List candidatePlan = new();
public IAction ActivateAction => activateAction;
public List ActivatePlan { get; private set; } = new();
private IGoal candidateGoal;
- private List candidateGoals = new();
+ private readonly 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. " +
"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);
+ 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," +
@@ -52,12 +52,12 @@ public int Compare(IGoal x, IGoal y)
private bool skipSearchWhenActionRunning;
internal bool SkipSearchWhenActionRunning => skipSearchWhenActionRunning;
public List Behaviors => Enumerable.Empty().Concat(actions.OfType()).Concat(goals.OfType()).ToList();
- public UnityEngine.Object _Object => gameObject;
+ public Object Object => gameObject;
public event System.Action OnUpdatePlanEvent;
private GOAPJobRunner jobRunner;
private bool isDirty = false;
[SerializeField]
- private bool isActive;
+ private bool isActive = true;
private bool activateFlag = false;
private void Awake()
{
@@ -107,7 +107,7 @@ private void LateUpdate()
if (tickType.HasFlag(TickType.ManualActivatePlanner))
{
isActive = false;
- if (logSearch) PlannerLog("Manual plan updating ends, need to be activated manully again.", bold: true);
+ if (LogSearch) PlannerLog("Manual plan updating ends, need to be activated manully again.", bold: true);
}
}
}
@@ -142,15 +142,15 @@ internal void SetCandidate(List path, IGoal goal)
candidatePlan.Clear();
candidateAction = null;
candidateGoal = null;
- if (logFail) PlannerLog("No candiate goal or path was found.");
+ 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}");
+ if (candidateAction != action && LogSearch) PlannerLog($"Search candidate action:{action.Name}");
candidateAction = action;
- if (candidateGoal != goal && logSearch) PlannerLog($"Search candidate goal:{goal.Name}");
+ if (candidateGoal != goal && LogSearch) PlannerLog($"Search candidate goal:{goal.Name}");
candidateGoal = goal;
}
List IPlanner.GetAllActions() => actions;
@@ -185,7 +185,7 @@ 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();
@@ -194,7 +194,7 @@ private void StartCurrentBestAction()
{
ActivateAction?.OnDeactivate();
SetCurrentAction(candidateAction);
- if (logActive) ActivePlanLog($"Starting {ActivateAction.Name}");
+ if (LogActive) ActivePlanLog($"Starting {ActivateAction.Name}");
ActivateAction.OnActivate();
}
public void TickGoals()
@@ -214,7 +214,7 @@ private void OnTickActivePlan()
// Goal no longer viable
if (!ActivateGoal.PreconditionsSatisfied(worldState))
{
- if (logActive) ActivePlanLog(
+ if (LogActive) ActivePlanLog(
$"{ActivateGoal.Name} failed as preconditions are no longer satisfied",
bold: true
);
@@ -225,7 +225,7 @@ private void OnTickActivePlan()
// Plan no longer viable
if (!(ActivateAction.PreconditionsSatisfied(worldState)))
{
- if (logActive) ActivePlanLog(
+ if (LogActive) ActivePlanLog(
$"{ActivateAction.Name} failed as preconditions are no longer satisfied",
bold: true
);
@@ -236,7 +236,7 @@ private void OnTickActivePlan()
// Goal complete
if (ActivateGoal.ConditionsSatisfied(worldState))
{
- if (logActive) ActivePlanLog($"{ActivateGoal.Name} completed", bold: true);
+ if (LogActive) ActivePlanLog($"{ActivateGoal.Name} completed", bold: true);
OnCompleteOrFailActivePlan();
return;
}
@@ -266,7 +266,7 @@ private void GetHighestPriorityGoals(List chosenGoals)
//Searching for highest priority goal
if (goals == null || goals.Count == 0)
{
- if (logFail) PlannerLog("No goals found");
+ if (LogFail) PlannerLog("No goals found");
return;
}
for (int i = 0; i < goals.Count; i++)
@@ -274,7 +274,7 @@ private void GetHighestPriorityGoals(List chosenGoals)
if (!goals[i].PreconditionsSatisfied(worldState))
{
- if (logFail) PlannerLog($"{goals[i].Name} not valid as preconditions not satisfied");
+ if (LogFail) PlannerLog($"{goals[i].Name} not valid as preconditions not satisfied");
continue;
}
chosenGoals.Add(goals[i]);
@@ -287,7 +287,7 @@ private void GetHighestPriorityGoals(List chosenGoals)
///
List IPlanner.GetSortedGoalData()
{
- List goalData = new List();
+ List goalData = new();
for (int i = 0; i < goals.Count; i++)
{
goalData.Add(
diff --git a/Runtime/Component/GOAPWorldState.cs b/Runtime/Component/GOAPWorldState.cs
index 5e08bed..99fac50 100644
--- a/Runtime/Component/GOAPWorldState.cs
+++ b/Runtime/Component/GOAPWorldState.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using Kurisu.GOAP.Resolver;
using UnityEngine;
@@ -11,72 +12,94 @@ namespace Kurisu.GOAP
public class GOAPWorldState : MonoBehaviour
{
//Attached and specific to the GameObject
- private GOAPStateSet localState;
- public GOAPStateSet LocalState=>localState;
- [SerializeField,Tooltip("Absent key in boolStates treated the same as key = false")]
+ private GOAPStateSet localState;
+ public GOAPStateSet LocalState => localState;
+ [SerializeField, Tooltip("Absent key in boolStates treated the same as key = false")]
private bool defaultFalse = true;
///
/// Shared between objects.
/// All states assumed to have g_ prefix
///
[SerializeField]
- private GOAPStateSet globalState;
- public GOAPStateSet GlobalState=>globalState;
- private DictionarynodeTargets=new();
- protected void Awake(){
+ private GOAPStateSet globalState;
+ public GOAPStateSet GlobalState => globalState;
+ private readonly Dictionary nodeTargets = new();
+#if UNITY_EDITOR
+ //Editor hook for UIElement update
+ internal Action OnUpdate;
+ private void Update()
+ {
+ OnUpdate?.Invoke();
+ }
+#endif
+ protected void Awake()
+ {
localState = ScriptableObject.CreateInstance();
- localState.defaultFalse=defaultFalse;
+ localState.defaultFalse = defaultFalse;
//Since we create instance manually, we need to init it
localState.Init();
}
- public bool IsSubset(Dictionary state){
- foreach(var i in state){
- if (!InSet(i.Key, i.Value)){
+ public bool IsSubset(Dictionary state)
+ {
+ foreach (var i in state)
+ {
+ if (!InSet(i.Key, i.Value))
+ {
return false;
}
}
return true;
}
- public void RegisterNodeTarget(INode node,Transform target)
+ public void RegisterNodeTarget(INode node, Transform target)
{
- nodeTargets[node]=target;
+ nodeTargets[node] = target;
}
public Transform ResolveNodeTarget(INode node)
{
- if(!nodeTargets.TryGetValue(node,out Transform target))
+ if (!nodeTargets.TryGetValue(node, out Transform target))
{
return null;
}
return target;
}
- public void SetState(string name, bool value,bool global=false){
- if (global){
+ public void SetState(string name, bool value, bool global = false)
+ {
+ if (global)
+ {
globalState.AddState(name, value);
}
- else{
+ else
+ {
localState.AddState(name, value);
}
}
- public void RemoveState(string name,bool includeGlobal=true){
- if (includeGlobal&&globalState!=null&&globalState.InStates(name)){
+ public void RemoveState(string name, bool includeGlobal = true)
+ {
+ if (includeGlobal && globalState != null && globalState.InStates(name))
+ {
globalState.RemoveState(name);
}
- else{
+ else
+ {
localState.RemoveState(name);
}
}
- public bool GetState(string name,bool includeGlobal=true){
- if (includeGlobal&&globalState!=null&&globalState.InStates(name)){
+ public bool GetState(string name, bool includeGlobal = true)
+ {
+ if (includeGlobal && globalState != null && globalState.InStates(name))
+ {
return globalState.GetState(name);
}
return localState.GetState(name);
}
- public bool InSet(string name, bool value,bool includeGlobal=true){
- if (includeGlobal&&globalState!=null&&globalState.InStates(name)){
+ public bool InSet(string name, bool value, bool includeGlobal = true)
+ {
+ if (includeGlobal && globalState != null && globalState.InStates(name))
+ {
return globalState.InSet(name, value);
}
return localState.InSet(name, value);
diff --git a/Runtime/Config/GOAPActionSet.cs b/Runtime/Config/GOAPActionSet.cs
index 1707a93..90ecb29 100644
--- a/Runtime/Config/GOAPActionSet.cs
+++ b/Runtime/Config/GOAPActionSet.cs
@@ -1,32 +1,33 @@
+using System;
using System.Collections.Generic;
using UnityEngine;
namespace Kurisu.GOAP
{
[CreateAssetMenu(fileName = "GOAPActionSet", menuName = "AkiGOAP/GOAPActionSet")]
- public class GOAPActionSet : ScriptableObject,IGOAPSet
+ public class GOAPActionSet : ScriptableObject, IGOAPSet
{
- [System.Serializable]
+ [Serializable]
private class ActionInternalSet
{
[SerializeReference]
- internal List actions=new List();
+ internal List actions = new();
internal ActionInternalSet(List behaviors)
{
- foreach(var behavior in behaviors)
+ foreach (var behavior in behaviors)
{
- if(behavior is IAction)actions.Add(behavior as IAction);
+ if (behavior is IAction) actions.Add(behavior as IAction);
}
}
}
[SerializeReference]
- private List behaviors=new();
- #if UNITY_EDITOR
- [Multiline(6),SerializeField]
+ private List behaviors = new();
+#if UNITY_EDITOR
+ [Multiline(6), SerializeField]
public string Description;
- #endif
- public List Behaviors=>behaviors;
- public Object _Object=>this;
-
+#endif
+ public List Behaviors => behaviors;
+ public UnityEngine.Object Object => this;
+
public List GetActions()
{
return JsonUtility.FromJson(JsonUtility.ToJson(new ActionInternalSet(behaviors))).actions;
diff --git a/Runtime/Config/GOAPGoalSet.cs b/Runtime/Config/GOAPGoalSet.cs
index aa992de..af4455d 100644
--- a/Runtime/Config/GOAPGoalSet.cs
+++ b/Runtime/Config/GOAPGoalSet.cs
@@ -1,32 +1,33 @@
+using System;
using System.Collections.Generic;
using UnityEngine;
namespace Kurisu.GOAP
{
[CreateAssetMenu(fileName = "GOAPGoalSet", menuName = "AkiGOAP/GOAPGoalSet")]
- public class GOAPGoalSet : ScriptableObject,IGOAPSet
+ public class GOAPGoalSet : ScriptableObject, IGOAPSet
{
- [System.Serializable]
+ [Serializable]
private class GoalInternalSet
{
[SerializeReference]
- internal List goals=new List();
+ internal List goals = new();
internal GoalInternalSet(List behaviors)
{
- foreach(var behavior in behaviors)
+ foreach (var behavior in behaviors)
{
- if(behavior is IGoal)goals.Add(behavior as IGoal);
+ if (behavior is IGoal) goals.Add(behavior as IGoal);
}
}
}
[SerializeReference]
- private List behaviors=new();
- #if UNITY_EDITOR
- [Multiline(6),SerializeField]
+ private List behaviors = new();
+#if UNITY_EDITOR
+ [Multiline(6), SerializeField]
public string Description;
- #endif
- public List Behaviors=>behaviors;
- public Object _Object=>this;
-
+#endif
+ public List Behaviors => behaviors;
+ public UnityEngine.Object Object => this;
+
public List GetGoals()
{
return JsonUtility.FromJson(JsonUtility.ToJson(new GoalInternalSet(behaviors))).goals;
diff --git a/Runtime/Config/GOAPSet.cs b/Runtime/Config/GOAPSet.cs
index dc6e6e5..226c2c7 100644
--- a/Runtime/Config/GOAPSet.cs
+++ b/Runtime/Config/GOAPSet.cs
@@ -1,44 +1,45 @@
+using System;
using System.Collections.Generic;
using UnityEngine;
namespace Kurisu.GOAP
{
[CreateAssetMenu(fileName = "GOAPSet", menuName = "AkiGOAP/GOAPSet")]
- public class GOAPSet : ScriptableObject,IGOAPSet
+ public class GOAPSet : ScriptableObject, IGOAPSet
{
- [System.Serializable]
+ [Serializable]
private class GoalInternalSet
{
[SerializeReference]
- internal List goals=new List();
+ internal List goals = new();
internal GoalInternalSet(List behaviors)
{
- foreach(var behavior in behaviors)
+ foreach (var behavior in behaviors)
{
- if(behavior is IGoal)goals.Add(behavior as IGoal);
+ if (behavior is IGoal) goals.Add(behavior as IGoal);
}
}
}
- [System.Serializable]
+ [Serializable]
private class ActionInternalSet
{
[SerializeReference]
- internal List actions=new List();
+ internal List actions = new();
internal ActionInternalSet(List behaviors)
{
- foreach(var behavior in behaviors)
+ foreach (var behavior in behaviors)
{
- if(behavior is IAction)actions.Add(behavior as IAction);
+ if (behavior is IAction) actions.Add(behavior as IAction);
}
}
}
[SerializeReference]
- private List behaviors=new();
- #if UNITY_EDITOR
- [Multiline(6),SerializeField]
+ private List behaviors = new();
+#if UNITY_EDITOR
+ [Multiline(6), SerializeField]
public string Description;
- #endif
- public List Behaviors=>behaviors;
- public Object _Object=>this;
+#endif
+ public List Behaviors => behaviors;
+ public UnityEngine.Object Object => this;
public List GetGoals()
{
diff --git a/Runtime/Config/GOAPStateSet.cs b/Runtime/Config/GOAPStateSet.cs
index 11906b9..994da90 100644
--- a/Runtime/Config/GOAPStateSet.cs
+++ b/Runtime/Config/GOAPStateSet.cs
@@ -10,53 +10,63 @@ namespace Kurisu.GOAP
public class GOAPStateSet : ScriptableObject
{
private Dictionary states;
- [SerializeField,Tooltip("Absent key in boolStates treated the same as key = false")]
+ [SerializeField, Tooltip("Absent key in boolStates treated the same as key = false")]
internal bool defaultFalse = true;
- internal void Init(){
+ internal void Init()
+ {
states = new Dictionary();
}
- public virtual void AddState(string name, bool value){
+ public virtual void AddState(string name, bool value)
+ {
states[name] = value;
}
- public void RemoveState(string name){
+ public void RemoveState(string name)
+ {
states.Remove(name);
}
- public bool GetState(string name){
- if (defaultFalse && !states.ContainsKey(name)){
+ public bool GetState(string name)
+ {
+ if (defaultFalse && !states.ContainsKey(name))
+ {
return false;
}
return states[name];
}
- public bool InStates(string name){
+ public bool InStates(string name)
+ {
return states.ContainsKey(name);
}
- public bool InSet(string name, bool value){
- if (!InStates(name)){
- return defaultFalse && value==false ? true : false;
+ public bool InSet(string name, bool value)
+ {
+ if (!InStates(name))
+ {
+ return defaultFalse && value == false;
}
- if (states[name] != value){
+ if (states[name] != value)
+ {
return false;
}
return true;
}
- #if UNITY_EDITOR
+#if UNITY_EDITOR
///
- /// In editor, we use OnValidate to init dictionary. If you want to serialize the value, try implement 'ISerializationCallbackReceiver'.
+ /// In editor, we use OnValidate to init dictionary.
+ /// If you want to serialize the value, try implement 'ISerializationCallbackReceiver'.
///
private void OnValidate()
{
Init();
- }
- #else
+ }
+#else
///
- /// Awake of ScriptableObject will be called when the file is created at first, useful in build game
+ /// Awake of ScriptableObject will be called when the file is unsearialized at first, useful in build game
///
private void Awake()
{
Init();
}
- #endif
+#endif
}
}
diff --git a/Runtime/Interface/IAction.cs b/Runtime/Interface/IAction.cs
index 9bc59f3..8865e5e 100644
--- a/Runtime/Interface/IAction.cs
+++ b/Runtime/Interface/IAction.cs
@@ -6,19 +6,19 @@ namespace Kurisu.GOAP
/// interface GOAP.IAction
/// Basic interface for all GOAPActions
///
- public interface IAction:INode
+ public interface IAction : INode
{
float GetCost();
void Init(GOAPWorldState worldState);
bool SatisfiesConditions(Dictionary conditions);
- bool PreconditionsSatisfied(GOAPWorldState worldState);
+ bool PreconditionsSatisfied(GOAPWorldState worldState);
void OnTick();
void OnActivate();
void OnDeactivate();
- public Dictionary preconditions{get;}
+ public Dictionary Preconditions { get; }
// What will be in worldState when action completed
- public Dictionary effects{get;}
- public string Name{get;}
-
+ public Dictionary Effects { get; }
+ public string Name { get; }
+
}
}
\ No newline at end of file
diff --git a/Runtime/Interface/IGOAPSet.cs b/Runtime/Interface/IGOAPSet.cs
index ed6abd5..483830d 100644
--- a/Runtime/Interface/IGOAPSet.cs
+++ b/Runtime/Interface/IGOAPSet.cs
@@ -3,7 +3,7 @@ namespace Kurisu.GOAP
{
public interface IGOAPSet
{
- List Behaviors{get;}
- UnityEngine.Object _Object{get;}
+ List Behaviors { get; }
+ UnityEngine.Object Object { get; }
}
}
diff --git a/Runtime/Interface/IGoal.cs b/Runtime/Interface/IGoal.cs
index d655e0a..5293970 100644
--- a/Runtime/Interface/IGoal.cs
+++ b/Runtime/Interface/IGoal.cs
@@ -5,7 +5,7 @@ namespace Kurisu.GOAP
///
/// Basic interface for all GOAPGoals
///
- public interface IGoal:INode
+ public interface IGoal : INode
{
///
/// Set static or dynamic priority of this goal
@@ -21,10 +21,10 @@ public interface IGoal:INode
void OnTick();
void OnActivate();
void OnDeactivate();
- Dictionary conditions{get;}
+ Dictionary Conditions { get; }
// What must be in worldState for the goal to be considered
- Dictionary preconditions{get;}
- string Name{get;}
+ Dictionary Preconditions { get; }
+ string Name { get; }
}
}
\ No newline at end of file
diff --git a/Runtime/Interface/IPlanner.cs b/Runtime/Interface/IPlanner.cs
index 9ba82b8..9cbf3cf 100644
--- a/Runtime/Interface/IPlanner.cs
+++ b/Runtime/Interface/IPlanner.cs
@@ -10,7 +10,7 @@ public interface IPlanner : IGOAPSet
List GetAllActions();
event System.Action OnUpdatePlanEvent;
GOAPWorldState WorldState { get; }
- int activeActionIndex { get; }
+ int ActiveActionIndex { get; }
List GetSortedGoalData();
void TickGoals();
void ManualActivate();
diff --git a/Runtime/Model/GOAPAction.cs b/Runtime/Model/GOAPAction.cs
index 37fe81f..93a1e72 100644
--- a/Runtime/Model/GOAPAction.cs
+++ b/Runtime/Model/GOAPAction.cs
@@ -6,58 +6,60 @@ namespace Kurisu.GOAP
/// A behavior that requires preconditions to run and has known
/// effects upon completion.
///
- public abstract class GOAPAction : GOAPBehavior,IAction
+ public abstract class GOAPAction : GOAPBehavior, IAction
{
protected GOAPWorldState worldState;
// All of these states are removed from worldState when OnDeactivate is called
- private Dictionary temporaryState;
+ private Dictionary temporaryState;
// What must be in worldState for the action to run
- public Dictionary preconditions{get; protected set;} = new Dictionary();
+ public Dictionary Preconditions { get; protected set; } = new Dictionary();
// What will be in worldState when action completed
- public Dictionary effects{get; protected set;} = new Dictionary();
+ public Dictionary Effects { get; protected set; } = new Dictionary();
// Absent key treated the same as key = false in preconditions and effects
- protected virtual bool DefaultFalse => true;
- public virtual string Name=>GetType().Name;
+ protected virtual bool DefaultFalse => true;
+ public virtual string Name => GetType().Name;
private GOAPState[] _effects;
- public GOAPState[] Effects
+ public GOAPState[] EffectStates
{
get
{
- if(DynamicSetEffect)
+ if (DynamicSetEffect)
{
- effects.Clear();
+ Effects.Clear();
SetupEffects();
- _effects= effects.Select(x=>new GOAPState(x)).ToArray();
+ _effects = Effects.Select(x => new GOAPState(x)).ToArray();
}
return _effects;
}
set
{
- _effects=value;
+ _effects = value;
}
}
- public GOAPState[] Conditions {get;private set;}
+ public GOAPState[] ConditionStates { get; private set; }
///
/// Whether effect of this action can be set dynamically at runtime
///
- protected virtual bool DynamicSetEffect=>false;
+ protected virtual bool DynamicSetEffect => false;
void IAction.Init(GOAPWorldState worldState)
{
- this.worldState =worldState;
+ this.worldState = worldState;
SetupDerived();
SetupEffects();
- if(!DynamicSetEffect)Effects= effects.Select(x=>new GOAPState(x)).ToArray();
- Conditions = preconditions.Select(x=>new GOAPState(x)).ToArray();
+ if (!DynamicSetEffect) EffectStates = Effects.Select(x => new GOAPState(x)).ToArray();
+ ConditionStates = Preconditions.Select(x => new GOAPState(x)).ToArray();
ResetTemporaryState();
}
- private void ResetTemporaryState(){
- temporaryState = new Dictionary();
+ private void ResetTemporaryState()
+ {
+ temporaryState = new Dictionary();
}
- public virtual float GetCost(){
+ public virtual float GetCost()
+ {
return 0f;
}
///
@@ -67,18 +69,21 @@ public virtual float GetCost(){
///
public bool SatisfiesConditions(Dictionary conditions)
{
- if(DynamicSetEffect)
+ if (DynamicSetEffect)
{
- effects.Clear();
+ Effects.Clear();
SetupEffects();
}
- foreach(var i in conditions){
+ foreach (var i in conditions)
+ {
//If condition not in the effects
- if (!effects.ContainsKey(i.Key)){
- if(DefaultFalse && i.Value==false)continue;
+ if (!Effects.ContainsKey(i.Key))
+ {
+ if (DefaultFalse && i.Value == false) continue;
else return false;
}
- if (effects[i.Key] != i.Value){
+ if (Effects[i.Key] != i.Value)
+ {
return false;
}
}
@@ -87,47 +92,53 @@ public bool SatisfiesConditions(Dictionary conditions)
///
/// Called when selected by GOAPPlanner
///
- public void OnActivate(){
+ public void OnActivate()
+ {
OnActivateDerived();
}
- protected virtual void OnActivateDerived(){}
+ protected virtual void OnActivateDerived() { }
///
/// Called by GOAPPlanner when action effects achieved or plan cancelled
///
- public void OnDeactivate(){
+ public void OnDeactivate()
+ {
OnDeactivateDerived();
ClearTemporaryStates();
}
- protected virtual void OnDeactivateDerived(){}
+ protected virtual void OnDeactivateDerived() { }
///
/// Called every frame by GOAPPlanner
///
- public virtual void OnTick(){}
+ public virtual void OnTick() { }
///
/// True if worldState is a superset of preconditions
///
///
///
- public bool PreconditionsSatisfied(GOAPWorldState worldState){
- return(worldState.IsSubset(preconditions));
+ public bool PreconditionsSatisfied(GOAPWorldState worldState)
+ {
+ return (worldState.IsSubset(Preconditions));
}
///
/// Effects can Setup at runtime to decrease actions since you can make brunch actions into one using dynamic effects setting
///
- protected virtual void SetupEffects(){}
+ protected virtual void SetupEffects() { }
///
/// Transform target and conditions can SetUp at runtime
///
- protected virtual void SetupDerived(){}
- protected void AddTemporaryState(string name, bool val){
+ protected virtual void SetupDerived() { }
+ protected void AddTemporaryState(string name, bool val)
+ {
worldState.SetState(name, val);
- temporaryState[name]=val;
+ temporaryState[name] = val;
}
- protected void ClearTemporaryStates(){
- foreach(var i in temporaryState){
- worldState.RemoveState(i.Key);
+ protected void ClearTemporaryStates()
+ {
+ foreach (var i in temporaryState)
+ {
+ worldState.RemoveState(i.Key);
}
ResetTemporaryState();
}
diff --git a/Runtime/Model/GOAPBehavior.cs b/Runtime/Model/GOAPBehavior.cs
index db0d9bd..0a8009f 100644
--- a/Runtime/Model/GOAPBehavior.cs
+++ b/Runtime/Model/GOAPBehavior.cs
@@ -5,12 +5,12 @@ namespace Kurisu.GOAP
[Serializable]
public class GOAPBehavior
{
- #if UNITY_EDITOR
+#if UNITY_EDITOR
[HideInInspector]
- public string description=string.Empty;
- [SerializeField,HideInInspector]
+ internal string description = string.Empty;
+ [SerializeField, HideInInspector]
private string guid;
- public string GUID{get=>guid;set=>guid=value;}
- #endif
+ internal string GUID { get => guid; set => guid = value; }
+#endif
}
}
diff --git a/Runtime/Model/GOAPGoal.cs b/Runtime/Model/GOAPGoal.cs
index 311f51e..99e4f11 100644
--- a/Runtime/Model/GOAPGoal.cs
+++ b/Runtime/Model/GOAPGoal.cs
@@ -9,51 +9,54 @@ namespace Kurisu.GOAP
// preconditions required to attempt the goal, and an actionLayer that determines
// what layer of GOAPActions will be considered for valid action plans.
///
- public abstract class GOAPGoal : GOAPBehavior,IGoal
+ public abstract class GOAPGoal : GOAPBehavior, IGoal
{
- protected GOAPWorldState worldState;
+ protected GOAPWorldState worldState;
// What must be in worldState for the goal to be complete
- public Dictionary conditions{get; protected set;}
+ public Dictionary Conditions { get; protected set; }
// What must be in worldState for the goal to be considered
- public Dictionary preconditions{get; protected set;}
- GOAPState[] INode.Effects => null;
- public GOAPState[] Conditions {get;private set;}
- public virtual string Name=>GetType().Name;
+ public Dictionary Preconditions { get; protected set; }
+ GOAPState[] INode.EffectStates => null;
+ public GOAPState[] ConditionStates { get; private set; }
+ public virtual string Name => GetType().Name;
void IGoal.Init(GOAPWorldState worldState)
{
- conditions = new Dictionary();
- preconditions = new Dictionary();
+ Conditions = new Dictionary();
+ Preconditions = new Dictionary();
this.worldState = worldState;
SetupDerived();
- Conditions=conditions.Select(x=>new GOAPState(x)).ToArray();
+ ConditionStates = Conditions.Select(x => new GOAPState(x)).ToArray();
}
///
/// Set the complete condition of this goal
///
- protected virtual void SetupDerived(){}
- public virtual float GetPriority(){
+ protected virtual void SetupDerived() { }
+ public virtual float GetPriority()
+ {
return 0f;
}
- public virtual bool PreconditionsSatisfied(GOAPWorldState worldState){
+ public virtual bool PreconditionsSatisfied(GOAPWorldState worldState)
+ {
// Will return true if preconditions are empty
- return worldState.IsSubset(preconditions);
+ return worldState.IsSubset(Preconditions);
}
- public virtual bool ConditionsSatisfied(GOAPWorldState worldState){
- return worldState.IsSubset(conditions);
+ public virtual bool ConditionsSatisfied(GOAPWorldState worldState)
+ {
+ return worldState.IsSubset(Conditions);
}
///
/// Called every frame by GOAPPlanner
///
- public virtual void OnTick(){}
+ public virtual void OnTick() { }
///
/// Called when selected by GOAPPlanner
///
- public virtual void OnActivate(){}
+ public virtual void OnActivate() { }
///
/// Called by GOAPPlanner when goal achieved or plan cancelled
///
- public virtual void OnDeactivate(){}
+ public virtual void OnDeactivate() { }
}
}
\ No newline at end of file
diff --git a/Runtime/Model/GOAPState.cs b/Runtime/Model/GOAPState.cs
index 601ce35..17cce37 100644
--- a/Runtime/Model/GOAPState.cs
+++ b/Runtime/Model/GOAPState.cs
@@ -7,16 +7,16 @@ public class GOAPState
/// Generated state unique key
///
///
- public string UniqueID{get;private set;}
+ public string UniqueID { get; private set; }
public string Key;
public bool Value;
- private static string On="_on";
- private static string Off="_off";
- public GOAPState(KeyValuePair pair)
+ private static readonly string On = "_on";
+ private static readonly string Off = "_off";
+ public GOAPState(KeyValuePair pair)
{
- this.Key=pair.Key;
- this.Value=pair.Value;
- this.UniqueID=$"{pair.Key}{(this.Value?On:Off)}";
+ Key = pair.Key;
+ Value = pair.Value;
+ UniqueID = $"{pair.Key}{(Value ? On : Off)}";
}
}
}
diff --git a/Runtime/Resolver/GraphBuilder.cs b/Runtime/Resolver/GraphBuilder.cs
index e417ae0..fb69e85 100644
--- a/Runtime/Resolver/GraphBuilder.cs
+++ b/Runtime/Resolver/GraphBuilder.cs
@@ -6,21 +6,21 @@
namespace Kurisu.GOAP.Resolver
{
internal class GraphBuilder
- {
+ {
public Graph Build(IEnumerable nodesToBuild)
{
var nodes = nodesToBuild.ToNodes();
-
+
var graph = new Graph
{
RootNodes = nodes.RootNodes.ToList(),
};
var allNodes = nodes.RootNodes.Union(nodes.ChildNodes).ToArray();
-
+
var effectMap = this.GetEffectMap(allNodes);
var conditionMap = this.GetConditionMap(allNodes);
-
+
foreach (var node in nodes.RootNodes)
{
this.ConnectNodes(node, effectMap, conditionMap, graph);
@@ -33,19 +33,19 @@ private void ConnectNodes(Node node, Dictionary> effectMap, D
{
if (!graph.ChildNodes.Contains(node) && !node.IsRootNode)
graph.ChildNodes.Add(node);
-
+
foreach (var actionNodeCondition in node.Conditions)
{
if (actionNodeCondition.Connections.Any())
continue;
-
+
var key = actionNodeCondition.Condition.UniqueID;
if (!effectMap.ContainsKey(key))
continue;
-
+
actionNodeCondition.Connections = effectMap[key].ToArray();
-
+
foreach (var connection in actionNodeCondition.Connections)
{
connection.Effects.First(x => x.Effect.UniqueID == key).Connections = conditionMap[key].ToArray();
@@ -61,16 +61,16 @@ private void ConnectNodes(Node node, Dictionary> effectMap, D
private Dictionary> GetEffectMap(Node[] actionNodes)
{
var map = new Dictionary>();
-
+
foreach (var actionNode in actionNodes)
{
foreach (var actionNodeEffect in actionNode.Effects)
{
var key = actionNodeEffect.Effect.UniqueID;
-
+
if (!map.ContainsKey(key))
map[key] = new List();
-
+
map[key].Add(actionNode);
}
}
@@ -81,16 +81,16 @@ private Dictionary> GetEffectMap(Node[] actionNodes)
private Dictionary> GetConditionMap(Node[] actionNodes)
{
var map = new Dictionary>();
-
+
foreach (var actionNode in actionNodes)
{
foreach (var actionNodeConditions in actionNode.Conditions)
{
var key = actionNodeConditions.Condition.UniqueID;
-
+
if (!map.ContainsKey(key))
map[key] = new List();
-
+
map[key].Add(actionNode);
}
}
@@ -102,8 +102,8 @@ internal static class Extensions
{
public static (Node[] RootNodes, Node[] ChildNodes) ToNodes(this IEnumerable nodes)
{
- var mappedNodes =nodes.Select(ToNode).ToArray();
-
+ var mappedNodes = nodes.Select(ToNode).ToArray();
+
return (
mappedNodes.Where(x => x.IsRootNode).ToArray(),
mappedNodes.Where(x => !x.IsRootNode).ToArray()
@@ -115,11 +115,11 @@ private static Node ToNode(INode node)
return new Node
{
InternalNode = node,
- Conditions = node.Conditions?.Select(y => new NodeCondition
+ Conditions = node.ConditionStates?.Select(y => new NodeCondition
{
Condition = y
}).ToList() ?? new List(),
- Effects = node.Effects?.Select(y => new NodeEffect
+ Effects = node.EffectStates?.Select(y => new NodeEffect
{
Effect = y
}).ToList() ?? new List()
diff --git a/Runtime/Resolver/Interface/INode.cs b/Runtime/Resolver/Interface/INode.cs
index 0cd4e23..b5bf73d 100644
--- a/Runtime/Resolver/Interface/INode.cs
+++ b/Runtime/Resolver/Interface/INode.cs
@@ -5,7 +5,7 @@ namespace Kurisu.GOAP.Resolver
{
public interface INode
{
- GOAPState[] Effects { get; }
- GOAPState[] Conditions { get; }
+ GOAPState[] EffectStates { get; }
+ GOAPState[] ConditionStates { get; }
}
}
diff --git a/Runtime/Resolver/Model/Node.cs b/Runtime/Resolver/Model/Node.cs
index ca0cc27..d4390f7 100644
--- a/Runtime/Resolver/Model/Node.cs
+++ b/Runtime/Resolver/Model/Node.cs
@@ -10,6 +10,6 @@ internal class Node
public INode InternalNode { get; set; }
public List Effects { get; set; } = new List();
public List Conditions { get; set; } = new List();
- public bool IsRootNode => this.InternalNode.Effects == null || !this.InternalNode.Effects.Any();
+ public bool IsRootNode => this.InternalNode.EffectStates == null || !this.InternalNode.EffectStates.Any();
}
}
diff --git a/Runtime/Runner/GoapJobRunner.cs b/Runtime/Runner/GoapJobRunner.cs
index 9c393e5..efdbc74 100644
--- a/Runtime/Runner/GoapJobRunner.cs
+++ b/Runtime/Runner/GoapJobRunner.cs
@@ -18,17 +18,17 @@ public class GOAPJobRunner
public GOAPJobRunner(GOAPPlannerPro planner, IGraphResolver graphResolver)
{
this.planner = planner;
- this.resolver = graphResolver;
+ resolver = graphResolver;
- this.executableBuilder = this.resolver.GetExecutableBuilder();
- this.positionBuilder = this.resolver.GetPositionBuilder();
- this.costBuilder = this.resolver.GetCostBuilder();
- this.conditionBuilder = this.resolver.GetConditionBuilder();
+ executableBuilder = resolver.GetExecutableBuilder();
+ positionBuilder = resolver.GetPositionBuilder();
+ costBuilder = resolver.GetCostBuilder();
+ conditionBuilder = resolver.GetConditionBuilder();
}
public void Run()
{
- this.resolveHandles.Clear();
+ resolveHandles.Clear();
RunInternal(planner);
}
@@ -40,47 +40,47 @@ private void RunInternal(GOAPPlannerPro planner)
return;
if (planner.ActivateAction != null && planner.SkipSearchWhenActionRunning)
return;
- this.FillBuilders(planner, planner.transform);
+ FillBuilders(planner, planner.transform);
//Create job for each candidate goal
foreach (var goal in planner.CandidateGoals)
- this.resolveHandles.Add(new JobRunHandle(goal, this.resolver.StartResolve(new RunData
+ resolveHandles.Add(new JobRunHandle(goal, 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),
+ StartIndex = resolver.GetIndex(goal),
+ IsExecutable = new NativeArray(executableBuilder.Build(), Allocator.TempJob),
+ Positions = new NativeArray(positionBuilder.Build(), Allocator.TempJob),
+ Costs = new NativeArray(costBuilder.Build(), Allocator.TempJob),
+ ConditionsMet = new NativeArray(conditionBuilder.Build(), Allocator.TempJob),
DistanceMultiplier = 1f
})));
}
private void FillBuilders(IPlanner agent, Transform transform)
{
- this.executableBuilder.Clear();
- this.positionBuilder.Clear();
- this.conditionBuilder.Clear();
+ executableBuilder.Clear();
+ positionBuilder.Clear();
+ conditionBuilder.Clear();
foreach (var node in agent.GetAllActions())
{
var allMet = true;
- foreach (var condition in node.Conditions)
+ foreach (var condition in node.ConditionStates)
{
if (!agent.WorldState.InSet(condition.Key, condition.Value))
{
allMet = false;
continue;
}
- this.conditionBuilder.SetConditionMet(condition, true);
+ conditionBuilder.SetConditionMet(condition, true);
}
- this.executableBuilder.SetExecutable(node, allMet);
- this.costBuilder.SetCost(node, node.GetCost());
- this.positionBuilder.SetPosition(node, agent.WorldState.ResolveNodeTarget(node)?.position ?? transform.position);
+ executableBuilder.SetExecutable(node, allMet);
+ costBuilder.SetCost(node, node.GetCost());
+ positionBuilder.SetPosition(node, agent.WorldState.ResolveNodeTarget(node)?.position ?? transform.position);
}
}
public void Complete()
{
bool find = false;
- foreach (var resolveHandle in this.resolveHandles)
+ foreach (var resolveHandle in resolveHandles)
{
if (find)
{
@@ -100,7 +100,7 @@ public void Complete()
find = true;
}
}
- this.resolveHandles.Clear();
+ resolveHandles.Clear();
if (!find)
planner.SetCandidate(resultCache, null);
}
@@ -112,7 +112,7 @@ public void Dispose()
resolveHandle.Handle.CompleteNonAlloc(ref resultCache);
}
- this.resolver.Dispose();
+ resolver.Dispose();
}
private struct JobRunHandle
@@ -121,8 +121,8 @@ private struct JobRunHandle
public IResolveHandle Handle { get; set; }
public JobRunHandle(IGoal goal, IResolveHandle handle)
{
- this.Goal = goal;
- this.Handle = handle;
+ Goal = goal;
+ Handle = handle;
}
}
}