nikkorn / mistreevous

A tool to build and execute behaviour trees in JavaScript and TypeScript
MIT License
65 stars 13 forks source link

Question about state/variables #97

Open Madd0g opened 1 week ago

Madd0g commented 1 week ago

Cool project!

Have you considered having variables support in the DSL? A context that the DSL could reference and the actions could update?

I'm kinda new to this, so maybe I'm missing something about state management, but it feels like being able to pass an object with data and being able to reference that from the DSL can be very useful.

Are any other examples or guides of complex state management, like:

Will appreciate any feedback!

nikkorn commented 4 days ago

Thanks very much @Madd0g ! 👍

Adding support for variables is something I've never really considered as I had designed this library in such a way that all state is the responsibility of the agent to manage, as opposed to other libraries out there which do handle some state for the agent, mainly in the form of a blackboard. Allowing the agent to manage its own state has its benefits, but I do agree that it would be very convenient to be able to pass variables to agent functions and not have to use literals everywhere. How we manage those variables, where they are stored/defined, and how we reference these in our mdsl/json is the real question though, do you have any ideas on how to maybe approach this?

Some initial ideas:

  1. Allow users to reference agent properties when defining action/condition/guard/callback function arguments. (easier)
  2. Each tree instance has a blackboard object (the initial state of this blackboard could be an optional BehaviourTree constructor parameter) and any conditions/actions can get/set values on the blackboard via methods on the tree instance. Any properties on the blackboard can be referenced when defining action/condition/guard/callback function arguments.

what if steps need outputs from previous steps (partially the context thing addresses this)

This would be a case of setting some state on the agent as part of one step and then simply using this state in subsequent steps. E.g. You may have a FindPath action which does some path finding and sets an 'activePath' property on the agent, and the next action to be visited may be FollowActivePath which just uses that 'activePath' property to update the position of the agent. I havent looked too much into how other libraries which implement a blackboard would expect users to distribute state between the agent and the tree intance. I will have a read up on that.

dealing with failures

This is handled by the user by means of:

Assigning ids or extra props to nodes

This would only be something that is useful for debugging/visualisations. While it would be relatively easy to allow users to assign identifers and any custom data to a node definition in json it would be considerable more work to implement this in mdsl so i'd be inclined not to do it.

Do these answers help or were you looking for more general information about how these things work with behaviour trees in general and weren't being specific to this library?

Thanks again for the feedback, I really appreciate it, it's always great to get a fresh perspective.

Madd0g commented 3 days ago

Do these answers help or were you looking for more general information about how these things work with behaviour trees in general and weren't being specific to this library?

I'm a beginner in both the general and the specific, so everything definitely helps.

Thanks for replying, I'll respond about the variables (that's what I care about the most) and maybe come back later for a full response

but I do agree that it would be very convenient to be able to pass variables to agent functions and not have to use literals everywhere.

That's what I'm aiming for, not just to eliminate the hardcoded literals but also give the DSL more flexibility/control, with variables the DSL (not just the agent) can be more dynamic by referencing things from this blackboard object (not just use static literals, but also objects?)

Where they are stored - that's a problem due to the lack of support in JSON for anything other than simple types. Maybe variable-access could be wrapped in with a special wrapper {{obj.value}} or something in the JSON. But is there another place where this causes concerns?

root {
    condition [HasItem, "gold", 500]
}

Could be:

root {
    condition [HasItem, targetOre, minQuantityForSale]
}

And:

{
    "type": "root",
    "child": {
        "type": "condition",
        "call": "HasItem",
        "args": [
            "{{ctx.targetOre}}",
           "{{ctx.minQuantityForSale}}"
        ]
    }
}

Where's the blackboard?

Maybe it deserves a special object under the agent and not the agent itself - it's just allows for more interesting behaviors and limits scope.

One thing I did before in a similar situation is to use a Proxy object to resolve variable access upwards in whatever chain the code was in. So if you want to have separate blackboards - the inner one could transparently reach into the blackboard of the parent. This is kinda black magic and I'm not sure it's necessary here, but it allows for interesting compositions.

I (briefly) looked into other BT libraries and didn't really see anything with a DSL, I think in a code-only library variable/object access is just easier/different?

Thanks for considering this!