Closed shadowndacorner closed 9 months ago
Hi, Thanks for the feedback 🙂
it seems like the apply_effects function may run more than once as part of the pathfinding process.
Yes, during the "planning phase", the apply_effects()
will be called. More than once.
A copy of the initial state will be made, and mutated via the apply_effects()
until it is equal to the final state (the goal).
The real blackboard is left unmutated once the plan is found.
Just noticed the dry_run argument - not sure how I missed that haha.
It's ok lol I only added it in the v0.3.0
for the specific problem you mentioned. During the planning phase, this argument will be true
, during the plan execution it will be false
.
is the blackboard meant to contain a reference to the relevant agent/actor/etc
It's up to you. The actions can have a state (private members, a constructor, other methods, etc...). But the blackboard could hold that state as well.
I am using EnTT as an ECS. My blackboard contains a reference to the global game state, while my actions contain a reference to a view (iterable collection of entities with specific components). If the dry_run
argument is false, i will iterate over that view to apply changes to both the global game state and local state of entities.
I also make use of EnTT's event dispatcher to implement a message bus, so my actions can send some messages that are picked up later on.
is the planner thread-safe?
The planner works on a copy of the initial state. The only problem might be the actions. If your actions hold some state and are not thread-safe, and you share them between multiple planners (since they are basically std::shared_ptr
when they should be std::unique_ptr
but I failed to implement that because they are a pain to work with, cf this reddit thread).
Something like this:
auto stateful_action = std::make_shared<my_stateful_action>(...);
auto plan1 = planner<blackboard_type>(std::vector{stateful_action}, initial1, goal1);
auto plan2 = planner<blackboard_type>(std::vector{stateful_action}, initial2, goal2);
But if your actions are self-contained, stateless, or thread-safe, then there should be no problem.
I hope this helps 🙂
FYI: I decided to force myself to use std::unique_ptr
instead of std::shared_ptr
, I think I found a satisfying solution. More information in the PR #6
As a result, now the planner owns all the data that are given to it. Making it thread-safe.
I'm closing this issue since it has been resolved.
If you have any further question, don't hesitate to open a discussion 🙂
First off, great work on this library!
I'm a bit confused as to the intended pattern for actually executing the plans that come out of this GOAP implementation. If I'm understanding the API correctly, it seems like the apply_effects function may run more than once as part of the pathfinding process. That would seemingly make it a poor fit to hook into for eg updating an agent's nav target, or having them attack another agent, which are operations you only want to execute when you have a full plan.
Is my understanding here accurate, or am I misunderstanding how your implementation works? I'm also not seeing a way to get eg a queue of actions out of the planner, but maybe apply_effects is intended to work as essentially a visitor for that purpose?
Edit: Just noticed the dry_run argument - not sure how I missed that haha. Is that meant to differentiate between the planning process and the execution process? If so, is the blackboard meant to contain a reference to the relevant agent/actor/etc, with the planner and actions being single-instance for the application, or are they meant to be instanced for each agent? If the former, is the planner thread-safe?