{"id":19460704,"url":"https://github.com/toastisme/opengoap","last_synced_at":"2025-07-23T05:33:07.013Z","repository":{"id":45688187,"uuid":"468909029","full_name":"toastisme/OpenGOAP","owner":"toastisme","description":"An open source tool to design and monitor goal orientated action planning (GOAP) in Unity. ","archived":false,"fork":false,"pushed_at":"2022-09-23T21:26:14.000Z","size":562,"stargazers_count":20,"open_issues_count":0,"forks_count":6,"subscribers_count":4,"default_branch":"main","last_synced_at":"2023-03-10T08:09:45.902Z","etag":null,"topics":["ai","behavior","behaviour","goap","npc"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/toastisme.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-03-11T21:48:51.000Z","updated_at":"2023-03-03T01:08:25.000Z","dependencies_parsed_at":"2023-01-18T19:01:37.550Z","dependency_job_id":null,"html_url":"https://github.com/toastisme/OpenGOAP","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toastisme%2FOpenGOAP","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toastisme%2FOpenGOAP/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toastisme%2FOpenGOAP/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toastisme%2FOpenGOAP/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/toastisme","download_url":"https://codeload.github.com/toastisme/OpenGOAP/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223990191,"owners_count":17237230,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","behavior","behaviour","goap","npc"],"created_at":"2024-11-10T17:38:15.933Z","updated_at":"2024-11-10T17:38:16.465Z","avatar_url":"https://github.com/toastisme.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OpenGOAP\n\nOpenGOAP is an open source tool to design and monitor goal orientated action planning in Unity.\n![OpenGOAP](https://github.com/toastisme/OpenGOAP/blob/main/Runtime/Screenshots/HarvestFoodPlan.PNG?raw=true)\n\n## Features\n- World state consisting of local (`GameObject` specific) and global states\n- A* planner for selecting the current optimum plan\n- Action layers to improve performance (e.g. avoid checking if sleeping is a viable action when running from danger)\n- GUI to visualise the current active plan and goals in priority order\n- Optional logger for additional debugging\n\n## Installation\n\nTested on Windows 10 using Unity 2021.2.11f1\n- Install Git (e.g. for Windows https://gitforwindows.org/)\n- In Unity open the package manager (`Window` --\u003e `Package Manager`)\n- Use the plus button to add a new package, and choose `Add package from git URL`\n- Add the URL `https://github.com/toastisme/OpenGOAP.git`\n\n## Getting Started\n\n### How OpenGOAP works\n\n- A `GameObject` using GOAP has a `GOAPPlanner`, `WorldState`, and a series of `GOAPGoal` and `GOAPAction` components. \n- The `GOAPPlanner` finds the `GOAPGoal` with the highest priority that has a viable action plan and executes that plan.\n- An action plan is a list of `GOAPActions`, and is viable if the final `GOAPAction` satisfies the conditions of the `GOAPGoal`, and each preceeding `GOAPAction` satisfies the conditions of the action that follows it, where the first `GOAPAction`'s conditions are satisfied by the current `WorldState`.\n- To find the optimum viable action plan the `GOAPPlanner` uses the [A* search algorithm](https://en.wikipedia.org/wiki/A*_search_algorithm) to find \nthe series of actions which have the minimum cost.\n\n#### GOAPGoal\n\nA `GOAPGoal` has a dictionary of boolean `conditions` that need to be met to satisfy the goal, and optionally a dictionary of boolean `preconditions` that must be met before it can be considered (beyond having a viable plan). Each `GOAPGoal` belongs to an `actionLayer` (string), which tells the planner only consider `GOAPActions` for this goal that have the same `actionLayer` (by default this is set to All, where all `GOAPActions` are considered). For this component the main interface is the following:\n- `SetupDerived()` called when the `GameObject` is first initialised\n- `OnActivate()` called the when goal is first selected by the `GOAPPlanner`\n- `OnDeactivate()` called when the goal is deselected by the `GOAPPlanner` (either due to completing the goal or finding a better one)\n- `GetPriority()` value between 0 and 1 \n- `PreconditionsSatisfied(WorldState)` can be used to avoid searching for action plans due to some known requirement\n- `ConditionsSatisfied(WorldState)` true if the aim of the goal is satisfied (i.e the current `WorldState` satisfies the `GOAPGoal` `conditions`)\n- `OnTick()` called every frame by the `GOAPPlanner` \n\n#### GOAPAction\n\nA `GOAPAction` has a dictionary of boolean `preconditions` that need to be met before it can run, and a dictionary of boolean `effects` that will occur as a result of running to completion. For this component the main interface is the following:\n- `SetupDerived()` called when the `GameObject` is first initialised. Used for e.g. getting components required for the action\n- `SetupEffects()` called when the `GameObject` is first initialised. Used to populate the effects boolean dictionary\n- `SetupActionLayers()` called when the `GameObject` is first initalised. Used to populate which `actionLayers` the action belongs to\n- `OnActivateDerived()` called the when action is first selected by the `GOAPPlanner`\n- `OnDeactivateDerived()` called when the action is deselected by the `GOAPPlanner` (either due to completing the action or changing plan)\n- `GetCost()` value between 0 and 1\n- `PreconditionsSatisfied(WorldState)` Can this action run based on `WorldState`\n- `EffectsSatisfied(WorldState)` Are the action's effects all present in `WorldState`\n- `SatisfiesConditions(Dictionary\u003cstring, bool\u003e)` Does this action satisfy all boolean conditions in the dictionary\n- `OnTick()` called every frame by the `GOAPPlanner`\n\n#### WorldState\n\nA `WorldState` is composed of two `StateSets`, one global and one local. The global `StateSet` is common to multiple `GameObjects`, whereas the local\n`StateSet` is specific to the `GameObject` the `WorldState` is attached to. A `StateSet` is simply several dictionaries of strings mapped to values (analogous to a blackboard for behaviour trees). \nThe `WorldState` differentiates between local and global states by assuming a `g_` prefix for all global states. \nI.e, if you call `WorldState.AddState(\"InDanger\", true)`, this would be added to the local `StateSet`, whereas `WorldState.AddState(\"g_InDanger\", true)` \nwould be added to the global `StateSet` and apply to all other `GameObjects` sharing the same `Stateset`.\nBy default, an absent boolean key is assumed to be equivalent to the key being present with a false value. This can be turned off using `SetGlobalDefaultFalse` and `SetLocalDefaultFalse` for the global ahd local `StateSets`, respectively. (The motivation for this is to avoid the need of requiring many boolean states to properly define a particular `WorldState`. For example, if the goal is to harvest wood, a viable plan could be to take wood from the wood store and put it back in. To avoid this it's simpler to have a `woodExtractedFromStore = true` effect added to a `Action_TakeWoodFromStore`, rather than having `woodExtractedFromStore = false` on all other approaches.)\n\n### HarvestWood Example\n\nConsider an agent that has a goal of harvesting wood. The goal script could look something like this:\n\n```\nusing UnityEngine;\nusing GOAP;\n\npublic class Goal_HarvestWood : GOAPGoal\n{\n\n    protected override void SetupDerived(){\n        conditions[\"WoodHarvested\"] = true; // GOAPPlanner will consider the goal complete when this condition is in the WorldState\n        actionLayer = \"Wood\"; // Only actions in this layer will be considered by the GOAPPlanner for this goal\n    }\n\n    public override float GetPriority()\n    {\n        /*\n         * Priority depends on the number of known people, \n         * the current global wood level (g_wood), and the \n         * amount of wood obtained from a single harvest (WoodExtractValue)\n         */\n    \n        float demand = worldState.GetFloatState(\"People\");\n        demand *= worldState.GetFloatState(\"WoodExtractValue\");\n        return 1/(1+(worldState.GetFloatState(\"g_Wood\")/demand));\n    }\n}\n```\nWe now need a series of `GOAPActions`, atleast one of which has `\"WoodHarvested\" == true` in their `effects` dictionary. One of these could be taking wood to the wood store:\n\n```\nusing UnityEngine;\nusing GOAP;\n\npublic class Action_TakeWoodToStore : GOAPAction\n{\n \n    protected override void SetupActionLayers(){\n        actionLayers.Add(\"Wood\"); // This action is in the same layer as Goal_HarvestWood. GOAPActions can belong to many actionLayers.\n    }\n    \n    protected override void SetupEffects(){\n        effects[\"WoodHarvested\"] = true; // This action satisfies the conditions of Goal_HarvestWood\n        effects[\"g_WoodAvailableAtStore\"] = true; // Lets other GameObjects know wood is at the store\n    }\n    \n    protected override void SetupConditions(){\n        preconditions[\"HoldingWood\"] = true; // Cannot perform this action unless holding wood\n    }\n    \n    public override float GetCost(){\n        return worldState.GetFloatState(\"Fatigue\"); // action cost increases with fatigue\n    }\n\n    protected override void OnActivateDerived(){\n        /* Called when the action is first selected by the GOAPPlanner.\n           Some code here could identify the wood store position */\n    }\n\n    protected override void OnDeactivateDerived(){\n        /* Called when the action is deselected by the GOAPPlanner. */\n    }\n\n    public override void OnTick()\n    {\n        /*\n         * Move towards wood store\n         * If at wood store deposit wood and call \n         * AddTemporaryState(\"WoodHarvested, true\") \n         * TemporaryStates are automatically removed when the action completes\n         * This is useful for states that are no longer relevant after the action completes, and saves you needing to \n         * remember to remove it manually.\n         */\n    }\n}\n```\n\nNote the use of `GOAPAction.AddTemporaryState(\"WoodHarvested, true\")`. In this case `WoodHarvested` is only true at the instant the action has been completed. `AddTemporaryState` is just a wrapper for `WorldState.AddState`, but the state is automatically removed as part of `GOAPAction.OnDeactivate`, ensuring the state is removed as soon as the action completes or is cancelled. \n\n`Action_TakeWoodToStore` has a precondition of `\"HoldingWood\" == true`, and so we could have another action `Action_PickUpWood`, which picks up the nearest wood, given the precondition `\"WoodNearby\"` is true. This preconditon in turn could be in the `effects` dictionary of both `Action_ChopDownTree` and `Action_LookAround`. The latter could have a higher cost than the former, and so would only be selected by the `GOAPPlanner` if, say, the `GameObject` did not have an axe. `Action_LookAround` could have no `preconditions`, and so would always be viable from the current `WorldState`. \n\nTo have a `GameObject` utilise these behaviours simply add the goal and action scripts, along with a `GOAPPlanner` and `WorldState` script to the `GameObject` as components. The global `StateSet` is kept on a separate (empty) `GameObject`. This can be added in the inspector on the `WorldState` component, or added in code via `WorldState.SetGlobalState(StateSet)`. \n\nAdding many `GOAPGoals` and `GOAPActions` can get messy in the inspector. To help with this the `ComponentGrouper` component can be added, which allows components to be grouped together and hidden. This was taken from the amazing people [here](https://forum.unity.com/threads/group-components-in-inspector.513931/) (the Kitsuba version).\n\n### Visualisation and Debugging\n\nThe `GOAPPlanner` has a boolean `Display Planner` in the inspector. If this is set to true, when clicking on the `GameObject` in the Hierarchy navigation bar, a `GUIPlanner` window will be displayed showing the current active plan, and the priorities of all goals. For a given `GOAPGoal`, if `PreconditionsSatisfied() == false`, the goal will be greyed out.\n\nAdditional debugging can be done by adding a `GOAPLogger` to the scene (`Packages/OpenGOAP/Runtime/Prefabs/GOAPLogger`). This contains a logger for the active plan and another for the planner, which can be assigned to a `GameObject`'s `GOAPPlanner` in the inspector. These can be turned on or off individually on the loggers themselves, in the inspector, by selecting `Show Logs`. The Planner logger will print log statements for each step through the A* process when finding the optimum plan. The ActivePlan logger will essentially print log statements with the same information as the `GUIPlanner` window, but this can still be useful (for example for identifying if plans are being selected/completing instantly, repeatedly, due to a condition in `WorldState` not being reset correctly).\n\n![OpenGOAP](https://github.com/toastisme/OpenGOAP/blob/main/Runtime/Screenshots/GOAPLoggers.PNG?raw=true)\n\n## Running Tests\n- In your project packages folder, open the manifest file and add `\"com.davidmcdonagh-opengoap\"` to `\"testables\"` (see the Enabling tests for a package section [here](https://docs.unity3d.com/Manual/cus-tests.html#tests)).\n- In the Editor open `Window -\u003e General -\u003e Test Runner\n- Under `PlayMode` you should now see tests for OpenGOAP\n\n# TODO\n- Partial plans\n- Fixed sequences of actions\n\n## Further Info\n- [Full documentation](https://toastisme.github.io/OpenGOAP/)\n- See also [OpenBehaviourTree](https://github.com/toastisme/OpenBehaviourTree)\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoastisme%2Fopengoap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftoastisme%2Fopengoap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoastisme%2Fopengoap/lists"}