Search Unity

Getting started with AI.Planner

Discussion in 'AI & Navigation Previews' started by Whitecold, Feb 11, 2020.

  1. Whitecold

    Whitecold

    Joined:
    Jan 29, 2015
    Posts:
    22
    I'd like to get started with using the AI Planner, and ran into a few troubles.
    • My custom Heuristic won`t be found by the AI.Planner builder, though it can be selected in the UI, when I try to play the scene, I get Temp\PlannerAssembly\AI.Planner.Actions\Basic\BasicExecutor.cs(29,124): error CS0246: The type or namespace name 'BasicNeedsHeuristic' could not be found (are you missing a using directive or an assembly reference?)
    • The otto project uses custom effects as effect of actions to update the needs, however in the newest 2.2 build of the project I can`t find any place to choose such special effects.
    • How can I update traits in scripts? I would like to increase my needs based on timeDelta each tick, but the TraitComponent returns me an ITraitData instead of an ITrait object, which I can`t cast back on my BasicNeed trait.
    • Is there any way I can return the success of an action in a Callback, lets say in case of a Navigation action where the path gets blocked?
     
    PaulUsul likes this.
  2. amirebrahimi_unity

    amirebrahimi_unity

    Unity Technologies

    Joined:
    Aug 12, 2015
    Posts:
    377
    First question - are you using AI.Planner 0.2.2-preview.1?

    I'll answer your questions in order:
     
  3. Whitecold

    Whitecold

    Joined:
    Jan 29, 2015
    Posts:
    22
    Thanks for your answers and clarifications. Yes, I am using the 0.2.2-preview.1 build
    • The problem is not that I can't select the heuristic in the inspector, the problem is that Unity then complains that it can't find it. I'll have a look at the Extensions page, I must have missed that one so far.
    • I did look at the newer projects, I only mentioned Otto because I wanted to know if there is a replacement of that functionality of having a custom function being called as effect. This would be very useful to implement conditional effects.
    • I did manage to update the values, now, manipulating values by their string names just feels very unclean
    • Thanks for your answer, I will have to do some more thinking in how to handle interrupted paths. I probably also have to think some more on how to handle blocked paths in the first place.
     
  4. Whitecold

    Whitecold

    Joined:
    Jan 29, 2015
    Posts:
    22
    I got my custom heuristic to work, the trick was to make a .asmdef and throw the code in there

    I am now trying to get some custom traits working, since I believe Traits referencing a game object will be a better way to change the values backing the trait, but I can't create the asset to connect to it like the 'location' asset. How do I make the asset and make it use the custom trait definition?
     
  5. TrevorUnity

    TrevorUnity

    Unity Technologies

    Joined:
    Jun 28, 2017
    Posts:
    101
    We pick up custom traits through the interface ICustomTrait, so make sure to add that to your custom struct. Keep in mind that as the data is stored in ECS buffers, all data in the custom trait must be blittable. Referencing managed types is not currently supported. If you look at the Location trait, we don't actually store the transform on the trait, only the position vector, forward vector, and instance ID. You might consider a blittable ID or other workaround for referencing game objects.
     
  6. Whitecold

    Whitecold

    Joined:
    Jan 29, 2015
    Posts:
    22
    I do use ICustomTrait, and I do use an int ID, I did base it on the location trait. The problem however is how to create the matching trait asset that is mentioned in https://docs.unity3d.com/Packages/com.unity.ai.planner@0.2/manual/CustomPlannerExtensions.html I did create a trait asset with the same name, how do I connect the two?
     
  7. TrevorUnity

    TrevorUnity

    Unity Technologies

    Joined:
    Jun 28, 2017
    Posts:
    101
    Ah. I see your issue now. I'll add a note to add extra documentation to make this clearer for our next release. In the meantime, let me give some clarity on how the custom traits and the trait assets are used:

    As you probably understand from diving into our system, the trait assets are used as serialized data so that you can author actions, termination conditions, etc that refer to the traits and trait properties. When we generate code, for any trait asset, if an implementation exists with the ICustomTrait interface, we'll skip its code generation. This ensures we don't create a conflicting type or overwrite the user implementation with any custom logic you may have added.

    Now, we don't automatically populate the trait assets with fields from custom code. It is currently up to the user to keep the trait asset in sync with the fields on the custom trait. However, due to the limited field types our serialization method supports, you won't be able to add fields of other types to the trait asset. You can still have fields of other types. You just cannot add them to the trait definition asset. If you want to use fields of custom types in preconditions/effects/rewards/etc, you'll need to write custom precondition/effect/reward/etc code to operate on that data.

    We're considering better methods of supporting custom code/types, but we're also avoiding trying to solve arbitrary data serialization for our authoring assets, which is beyond the scope of our package. We're open to feature requests, if you have anything specific in mind. Otherwise, stay tuned!
     
  8. Whitecold

    Whitecold

    Joined:
    Jan 29, 2015
    Posts:
    22
    I'm not quite sure I understand. The location asset definition has three properties, of which Transform is not blittable, but in actual use, Transform is the only field that can be assigned.
    I tried to do something similar with gameObject, to save an ID, but the autogenerated code still includes a regular ITrait struct, instead of my custom definition.

    Code (CSharp):
    1.  public struct Self : ICustomTrait, IEquatable<Self>
    2.     {
    3.         public int ID;
    4.         public GameObject Character
    5.         {
    6.             get => null;
    7.             set
    8.             {
    9.                 ID = Character.GetInstanceID();
    10.             }
    11.         }
    12.  
    13.         public bool Equals(Self other)
    14.         {
    15.             return ID == other.ID;
    16.         }
    17.  
    18.         public object GetField(string fieldName)
    19.         {
    20.             switch (fieldName)
    21.             {
    22.                 case nameof(ID):
    23.                     return ID;
    24.             }
    25.  
    26.             return null;
    27.         }
    28.  
    29.         public void SetField(string fieldName, object value)
    30.         {
    31.             switch (fieldName)
    32.             {
    33.                 case nameof(ID):
    34.                     ID = (int)value;
    35.                     break;
    36.                 case nameof(Character):
    37.                     Character = (GameObject)value;
    38.                     break;
    39.             }
    40.         }
    41.  
    42.         public override string ToString()
    43.         {
    44.             return $"My ID: {ID}";
    45.         }
    46.     }
     
  9. TrevorUnity

    TrevorUnity

    Unity Technologies

    Joined:
    Jun 28, 2017
    Posts:
    101
    Hmmm. Your code looks correct. Quick question: did you define Self in the namespace AI.Planner.Domains?
     
  10. Whitecold

    Whitecold

    Joined:
    Jan 29, 2015
    Posts:
    22
    I did try Unity.AI.Planner.DomainLanguage.TraitBased, Unity.AI.Planner.Domains, as well as my custom namespace, without success. An ITrait struct always gets generated.

    Also, is there a way to have Custom Preconditions and Actions without wrapping them in a separate Assembly, since I'd like to reference my main code in them.

    Is there a Reference what the diffierent namespaces are? There seems to be a UnityEngine.AI.Planner, Unity.AI.Planner, and AI.Planner.
     
  11. mplantady_unity

    mplantady_unity

    Unity Technologies

    Joined:
    Jun 19, 2019
    Posts:
    37
    You can check our TypeResolver.cs class to see what types can be retrieved, your ICustomTrait need to be in a namespace that is checked there.
    There is a hidden 'namespace' field on Trait assets that should be used during the compilation to retrieve a ICustomTrait in any namespace, but it seems we don't used it in the public version of the Planner. It will be fixed in a future version.

    It is required to have custom Planner code inside an assembly definition because our generated code have to reference these types directly.
    The preferred way to call shared game code, would be to put this code in another assembly definition, that you can reference, and use as a shared library in other parts of your code.

    Notes that we are still working on how this custom code flow is working and is likely to change in next update.
    We also plan to update the documentation to give some guidelines around that.

    Unity.AI.Planner : Planner package code
    UnityEngine.AI.Planner : Planner package code that have references to UnityEngine assembly
    AI.Planner.* : Namespace for generated code created from planner asset definitions
     
  12. Whitecold

    Whitecold

    Joined:
    Jan 29, 2015
    Posts:
    22
    I'm still having trouble with getting custom traits to work.
    A few features I think would be nice to have in the AI planner:
    • An description of assembly requirements would be nice to have in the documentation, especially since custom heuristics can be selected in planner assets when they are defined in other places, only on build they won't be found.
    • Methods on TraitComponent to check, add, remove and modify traits directly, without any need to mess with strings and ITraitData.
    • A way to specify a Trait Based Object as Actor of a DecisionController Instance, meaning it always has to be used as first object. This would greatly simplify any situation with multiple actors, which may need to access each others main object to plan interactions. This currently requires making a separate local ID object, checking that each and every time if it is the same ID on the actor object, leading to a lot of redundancy, making actions less legible.
    • I would like a way to easily get data from the game into traits, without having to write custom traits. One way I'd imagine would be an ITraitable<MyTrait> interface that can be implemented outside of any assemblies. ITraitable would have a function OnLoadWorldState(MyTrait trait) in which you can write all the data to the fields of the trait you defined before, and then return it. This way you can have traits backed by your game objects without having to write all the get/set stuff and dealing with assemblies and custom traits. The assemblies are very impractical, if my custom trait referencing my main character class has to go into an assembly, pretty much my entire game code has to go in that assembly.
     
  13. amirebrahimi_unity

    amirebrahimi_unity

    Unity Technologies

    Joined:
    Aug 12, 2015
    Posts:
    377
    I've made a note about this on our Favro board. Thank you.

    This is going away. Traits will be accessed directly by name and not through a TraitComponent.

    Have you tried any of the World Query options on the Decision Controller? There is a "From GameObject" filter that will constrain to including a specific GameObject. I think that might work better than the local object.

    Unfortunately, this isn't likely to be supported. As of now, for performance reasons and for use of multi-threading in jobs all types need to be defined explicitly. If we explore an "interpreted mode" for the planner in the future, then perhaps we can create a dynamic trait of some sort, but for now I wouldn't rely on this feature being added. Another possibility is that there could be a post-processing step that occurs for your own classes that can generate traits and provide some interop.
     
    Last edited: Mar 4, 2020
  14. Whitecold

    Whitecold

    Joined:
    Jan 29, 2015
    Posts:
    22
    If I have a TalkToPerson action it takes two Agent objects, the first one must be the one on the planner, the second one can be any other character.
    The problem is if my planner needs to have access for this to all Agent objects, and needs to know which one the planner itself is. Constraining doesn't help there.

    The types would be explicitly defined in the trait, the interface I imagined would be only filling in those defined fields when called upon.
    If I have a health component, I would like a trait with a health int field, the field being treated like it was a standard trait, until the planner loads the world state again. Then the trait should reload the health field from the health component. To my understanding this is pretty much how the location trait currently works.
     
  15. amirebrahimi_unity

    amirebrahimi_unity

    Unity Technologies

    Joined:
    Aug 12, 2015
    Posts:
    377
    Can you share a little bit more about this? Are these all NPCs or is one player controlled? What is the significance of having one of the agents as the first parameter (i.e. is there an effect that only happens for the first parameter)? I need to understand more about your model before being able to answer the question.

    I misunderstood your original question. Ideally, the traits will be used for the actual data storage. With our next release this will be much more direct than our current approach, which is via TraitComponent. I'm curious why your custom trait would be referencing the main character and not the other way around of your game code referencing traits and setting data on those. Either way, we can consider some helper scaffolding in the future as we learn more about how developers continue to use the planner. Thank you for your feedback.
     
  16. Whitecold

    Whitecold

    Joined:
    Jan 29, 2015
    Posts:
    22
    The idea is that I have identical characters A, B, C etc that have for example a socialNeed that can be reduced by talking to each other. I want to ensure that the planner A only plans talks between A and B or C, and not B talking to C, while planners B and C likewise ensure their actor is always involved in the action.


    I would consider the traits a model of the data for the planner, not the actual data. The effects of an action are the predicted changes, the actual changes are applied during the function callback assigned to an action.
    I absolutely want my data on a scriptableObject I can serialize, subclass and add whatever types I want without dealing with the restrictions of the traits. Also I very much want my main data classes not in any separate assembly.
    Also many of the character data may be irrelevant for the AI, and thus should not be in the planner and clutter up things.

    The model vs data abstraction also is absolutely necessary if the outcome is random. The action may assume the average damage dealt, or the average HP healed, but the action itself changes it by a random amount. Similarly, the effect may actually be applied over time, not instantaneously like the planner assumes.

    I can also think of cases where a trait is actually already amalagmated information, for example a single number representing the strength of a whole squad, in which case the squad should be polled to provide a strength value for the planner.
     
  17. amirebrahimi_unity

    amirebrahimi_unity

    Unity Technologies

    Joined:
    Aug 12, 2015
    Posts:
    377
    Thanks for the additional feedback. We discussed as a team and our current thoughts are to introduce a default trait (e.g. 'Planner' or 'DefaultAgent') on a trait-based object that also has a decision controller attached when initializing state for that planner. You can then use this default trait in any of your actions.

    Some of what you're mentioning is already supported with ICustomTrait (e.g. Location is an example of that). We're currently re-working quite a bit of how traits are serialized in preparation for DOTS workflow support, so let's revisit this discussion when that release is out.

    Re: random values -- the planner supports probabilistic outcomes from a single action, but that is not something that you can configure without digging into code (i.e. cannot make use of this through the UI). If you were going to have random outcomes, then you'd likely want to model an expectation value (read: avg) for an action instead and/or have different actions. It wouldn't be good to try and include randomness in a custom action effect for the planner as it would adversely affect the cost/reward of taking an action. The planner might pick that route as an optimal path even though in reality there is a low probability of getting a high or low value. It would be okay, however, to apply randomness in your callback when modifying the world state after taking a planner action.
     
  18. Whitecold

    Whitecold

    Joined:
    Jan 29, 2015
    Posts:
    22
    That sounds great.

    My comment was mostly designed as an example where I would like to have my data in two places, once in the world, and once separately on the trait which syncs it from the world. I'm sure the DOTS workflow will help there in any case, as you can write an system there to sync it in an efficient way, instead of relying on many Update() functions writing stuff to the traits that slow down the game.

    A few other improvements and features that came to my mind:
    • Flag enums and appropriate comparators would be a nice addition
    • Custom preconditions and custom actions that work only on selected traits, instead of the whole state array would be great, similar to how CustomRewards work currently. Best would be if they can take additional, selectable parameters like a float to make for example a precondition 'distance between two locations is smaller than x'
     
  19. TrevorUnity

    TrevorUnity

    Unity Technologies

    Joined:
    Jun 28, 2017
    Posts:
    101
    Our next release, coming this month, will rework our API around manually syncing state, enacting plans, etc. If you don't want to use TraitComponents to hold trait data in a 1-to-1 correspondence with the planner state, you will instead be able to manually construct a planner state from your own game state representation. You'll also be able to specify callbacks for when an action has completed, when the end of the plan has been reached, and when the agent has arrived at a state not covered in the plan. This should help you interface with our package more smoothly for your particular use case.

    Great suggestions! I'll add them to our feature request list.
     
unityunity