Custom Planner Extensions
In many cases, you may wish to include custom operators or types which cannot be authored by the package's inspector-based authoring tools. For this purpose, custom extensions can be added by providing code which implements any of the custom interfaces outlined below. Implementations of these interfaces will be detected and included by the relevant inspectors.
Note: For each interface, the type parameter TStateData
will correspond to the type StateData
, as generated by our domain code generation process.
Custom Traits
In circumstances in which you'd like to add custom methods or other logic to traits, you can provide your own trait by creating a struct which implements the ICustomTrait interface. In order to be used by our system, your custom struct must only contain fields which are blittable. Be sure to declare this custom struct within the namespace Generated.AI.Planner.StateRepresentation
inside a folder with an Assembly Definition Reference pointing to Generated.AI.Planner.StateRepresentation
assembly.
In order for the trait properties to be referenced by other authored assets, such as action definitions, you must also provide a trait definition asset for the custom trait specifying the field names and field types. If you specify a custom type for one of your trait's fields, ensure you have specified the fully qualified type name.
For an example of a custom trait, see Location.
Custom Methods for Planning
In order to be detected, your custom structs need to be inside a folder with an Assembly Definition Reference pointing to AI.Planner.Custom
assembly.
Action Preconditions
For preconditions more complicated than checking trait compositions and comparing trait property values, you can provide custom logic to evaluate an action precondition by creating a struct which implements ICustomActionPrecondition<TStateData>
. This interface requires the implementation of a single method:
bool CheckCustomPrecondition(TStateData state, ActionKey action) { ... }
where state
is a struct containing the object trait data and action
contains the indices of the parameters to evaluate. These indices correspond to the order of objects in both state.TraitBasedObjects
and state.TraitBasedObjectIds
.
Action Effects
For complex action effects, you can provide a struct which implements the interface ICustomActionEffect<TStateData>
. This interface requires the implementation of a single method:
void ApplyCustomActionEffectsToState(TStateData originalState, ActionKey action, TStateData newState) { ... }
where
originalState
contains the trait and object data for the state in which the action is taken,action
contains the indices of the parameters of the action, andnewState
is the new state data with all authored effects applied except for the removal of objects.
Action Rewards
For custom reward computation, you can provide a structs which implement the interface ICustomActionReward<TStateData>
. You will need to implement the method:
float RewardModifier(TStateData originalState, ActionKey action, TStateData newState) { ... }
where
originalState
contains the trait and object data for the state in which the action is taken,action
contains the indices of the parameters of the action, andnewState
is the new state data after all action effects have been applied.
Trait Rewards
For more simply computed rewards, you may instead override any of the ICustomTraitReward<...>
interfaces, shown below. These rewards are computed directly from up to three traits across any of the action parameters. This trait data is pulled from the original state, before any action effects have been applied. For an example, see the built-in custom trait reward, LocationDistance
, which computes a reward modifier proportional to the distance between two objects with the Location
trait.
interface ICustomTraitReward<TTrait1>
{
float RewardModifier(TTrait1 trait1);
}
interface ICustomTraitReward<TTrait1, TTrait2>
{
float RewardModifier(TTrait1 trait1, TTrait2 trait2);
}
interface ICustomTraitReward<TTrait1, TTrait2, TTrait3>
{
float RewardModifier(TTrait1 trait1, TTrait2 trait2, TTrait3 trait3);
}
Termination Preconditions
For preconditions in a termination state you can provide custom logic to evaluate a precondition by creating a struct which implements ICustomTerminationPrecondition<TStateData>
. This interface requires the implementation of a single method:
bool CheckCustomPrecondition(TStateData state) { ... }
where state
contains the trait and object data for the state in which the termination has been reached.
Termination Rewards
For custom reward computation in a termination state, you can provide a structs which implement the interface ICustomTerminationReward<TStateData>
. You will need to implement the method:
float RewardModifier(TStateData state) { ... }
where state
contains the trait and object data for the state in which the termination has been reached.
Cumulative Reward Estimators
As the planning algorithm used in this package is, at its core, a heuristic graph search, you may wish to provide a custom heuristic for predicting the future cumulative reward to be achieved from a given state. To do so, create a struct with implements the interface ICustomCumulativeRewardEstimator<TStateData>
, which requires the method
BoundedValue Evaluate(TStateData stateData) { ... }
where
stateData
is the state for which the estimate is made andBoundedValue
is an average/estimate, upper bound, and lower bound of the future cumulative reward from the given state onward. See BoundedValue.
For tips on designing a custom cumulative reward estimator, see Improving Performance