Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

[AI SCRIPTING] Panda BT: Behaviour Tree scripting

Discussion in 'Assets and Asset Store' started by ericbegue, Feb 26, 2016.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    do you have panda pro?
     
  2. TheGabelle

    TheGabelle

    Joined:
    Aug 23, 2013
    Posts:
    242
    Yes. I'm currently exploring Panda BT's capabilities and limitations. I have a game concept which would require many AI entities -- potentially thousands at a time.

    Test scene: 1 camera, no skybox. Test scripts on camera, spawned Entities as empty gameobjects with Entity script.

    Test 1: Test script spawned a variable number of Entities with a simple PandaBehaviour firing a Debug log every three seconds. Every 100 entities added had a noticeable frame rate hit. By the time I hit 20k I was down to 5-10 fps. Loads of garbage.

    Test 2: similar to Test 1, except the Panda Behaviours are ticked mannually. I subscribed them to an Action and invoked it every Nth of a second. At 2 ticks per second, 20k Entities at 15-20 fps. Huge profiler spikes. Less garbage.

    Test 3: similar to Test 1, except the script managed the Ticking of the PandaBehaviours. All Entities were divided into groups, and every tenth of a second one group would be Ticked (round robin, each group 2 ticks per second). 20k Entities at 20-30 fps.

    Test 4; similar to Test 3, except I only used one PandaBehaviour. I gave each Entity a serialized BTSnapshot. I manually looped through each Entity in a selected group, passed the only PandaBehaviour a respective Entity.snapshot, ticked, returned the snapshot to the Entity, and continued the loop. I had to wrap the snapshot logic in try catch. After a few ticks the try succeeded and everything worked. about 90 fps.

    I created Test 4 because the profiler showed FixedBehaviorUpdate and BehaviourTree.FixedUpdate() were chewing up cpu percentage, the later was invoked 4 times per entity.


    I would love to push Test 4 farther, but for that I feel more information about PTSnapshot is required. Test 4 is rough code, so clean up / optimization would likely save a few more frames.

    Any ideas?


    Test 4:
    Code (CSharp):
    1. tree("Root")
    2.     sequence
    3.         Hit
    4.  
    Code (CSharp):
    1.  
    2. [System.Serializable]
    3.     public class Entity : MonoBehaviour
    4.     {
    5.  
    6.         public int hitValue = 1;
    7.         public BTSnapshot snapshot;
    8.  
    9.         private void Start()
    10.         {
    11.             snapshot = new BTSnapshot();
    12.         }
    13.  
    14.     }
    15.  
    Code (CSharp):
    1.  
    2. public class BTController : MonoBehaviour
    3.     {
    4.  
    5.         public Entity entity;
    6.  
    7.         [Task]
    8.         void Hit()
    9.         {
    10.             if (entity != null)
    11.             {
    12.                 Test.hit(entity.hitValue);
    13.             }
    14.             Task.current.Succeed();
    15.         }
    16.     }
    17.  
    Code (CSharp):
    1.  
    2. public class Test : MonoBehaviour
    3.     {
    4.         public GameObject testObject;
    5.         PandaBehaviour pb;
    6.         BTController btc;
    7.         public int count;
    8.         public static int hits;
    9.         public static int lastHists;
    10.         float currentTime;
    11.         public float tickRate;
    12.         int ticksIndex = 0;
    13.         public int numberOfGroups;
    14.         Entity[] entities;
    15.  
    16.  
    17.         void Start()
    18.         {
    19.             pb = gameObject.GetComponent<PandaBehaviour>();
    20.             btc = gameObject.GetComponent<BTController>();
    21.             entities = new Entity[count];
    22.             currentTime = tickRate;
    23.             if (testObject != null)
    24.             {
    25.                 for (int i = 0; i < count; i++)
    26.                 {
    27.                     GameObject obj = Instantiate(testObject, transform);
    28.                     entities[i] = obj.GetComponent<Entity>();
    29.                     entities[i].hitValue = (i + 1);//Random.Range(0,10);
    30.                 }
    31.             }
    32.         }
    33.  
    34.         private void FixedUpdate()
    35.         {
    36.             currentTime += Time.fixedDeltaTime;
    37.             if (currentTime >= tickRate && pb != null && btc != null)
    38.             {
    39.                 lastHists = hits;
    40.                 for (int i = ticksIndex; i < entities.Length; i += numberOfGroups)
    41.                 {
    42.                     btc.entity = entities[i];
    43.                     if (pb != null && btc.entity != null && pb.snapshot != null && btc.entity.snapshot != null)
    44.                     {
    45.                         try
    46.                         {
    47.                             pb.snapshot = btc.entity.snapshot;
    48.                         }
    49.                         catch
    50.                         {
    51.                             Debug.Log("failed to apply snapshot @ i = " + i.ToString());
    52.                         }
    53.                     }
    54.                     else
    55.                     {
    56.                         Debug.Log("btc entity snapshot nodeStates == null");
    57.                     }
    58.  
    59.                     pb.Tick();
    60.  
    61.                     if (pb != null && btc.entity != null && pb.snapshot != null && btc.entity.snapshot != null)
    62.                     {
    63.                         try
    64.                         {
    65.                             btc.entity.snapshot = pb.snapshot;
    66.                         }
    67.                         catch
    68.                         {
    69.                             Debug.Log("failed to return snapshot @ i = " + i.ToString());
    70.                         }
    71.                     }
    72.                     else
    73.                     {
    74.                         Debug.Log("bp snapshot nodeStates == null");
    75.                     }
    76.                 }
    77.                 Debug.Log("group: " + ticksIndex.ToString() + " delta: " + (hits - lastHists).ToString());
    78.                 ticksIndex++;
    79.                 if (ticksIndex >= numberOfGroups) { ticksIndex = 0; }
    80.                 currentTime = 0f;
    81.             }
    82.         }
    83.  
    84.         public static void hit(int val)
    85.         {
    86.             hits += val;
    87.         }
    88.     }
    89.  
     
    Last edited: Mar 7, 2018
  3. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    I run 500 Panda entities easily, I think thousands would require you to call Tick() at staggered interval instead of every FixedUpdate like you do.
    panda does create garbage during instantiation but I don't think it creates garbage after that, Wait used to but has been fixed in the latest, and you don't use wait.

    Could you package your project in a way that I can easily deploy on my machine, this would be easier for me to look at it this way and also include Test2 that is causing snapshot error at line 59. Only error I know of is when your current.item is not [System.Serializable]
     
  4. TheGabelle

    TheGabelle

    Joined:
    Aug 23, 2013
    Posts:
    242
    I get this error when I don't try-catch the snapshot passing between entity and singlePandaBehaviour in Test 4. Test 4 is super clunky for the first 10 seconds or so, then hums along around 60 fps (earlier I said 90fps, but my script wasn't ticking all of them by mistake).

    Changing Test 4 to:
    count: 20000
    TickRate: 0.5 ( half second tick rate each entity )
    NumberOfGroups: 200

    Yields ~325 fps

    The same settings on Test3 yields ~15 fps. I could have created poor tests, but at this point using individual PandaBehaviours is a frame killer. If I can get snapshots working the way I hope they do then buying Pro will be best asset choice I've made in a long while.

    Tests 1-4 attached, ready to be dropped into any unity project with Panda Pro
     

    Attached Files:

    Last edited: Mar 8, 2018
  5. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    Clunky for the first 10 seconds is to be expected with panda. It unfortunately doesn't store the bytecode conversion in the prefab but recrunches it each time you instantiate it. You're probably seeing some massive GC spike too.
    I'll have a look at your zip.
     
  6. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    your code for test4 is really convoluted so it took more pocking than I'm used to.
    General recommendations:
    1. Stagger your instantiation.
    2. The way you're doing the ticking in Test4 is good, zero allocation : all good.
    3. use LogError instead of Log so you can filter messages in the console.
    4. never try catch early, it occludes problems
    with the try catch removed I can see the root of the problem:
    MonoDevelop_2018-03-05_21-48-34.png
    singleTaskBehaviour.entity.snapshot is never null, snapshot.nodeStates can be null, I think when the BT hasn't been ticket yet, to be verified.
    With this, test4 code should read:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Panda;
    5. using PBTTesting.Test4;
    6.  
    7. namespace PBTTesting.Test4
    8. {
    9.     public class Test : MonoBehaviour
    10.     {
    11.  
    12.         public GameObject testObject;
    13.  
    14.         PandaBehaviour singlePandaBehavior;
    15.  
    16.         TaskController singleTaskBehaviour;
    17.  
    18.         public int count;
    19.  
    20.         public static int hits;
    21.  
    22.         float currentTime;
    23.         public float tickRate;
    24.         float realTickRate;
    25.  
    26.         int currentGroup = 0;
    27.  
    28.         public int numberOfGroups;
    29.  
    30.         Entity[] entities;
    31.  
    32.  
    33.         void Start()
    34.         {
    35.             realTickRate = tickRate / numberOfGroups;
    36.             singlePandaBehavior = gameObject.GetComponent<PandaBehaviour>();
    37.             singleTaskBehaviour = gameObject.GetComponent<TaskController>();
    38.             entities = new Entity[count];
    39.  
    40.             currentTime = tickRate;
    41.  
    42.             if (testObject != null)
    43.             {
    44.                 for (int i = 0; i < count; i++)
    45.                 {
    46.                     GameObject obj = Instantiate(testObject, transform);
    47.                     entities[i] = obj.GetComponent<Entity>();
    48.                 }
    49.             }
    50.         }
    51.  
    52.         private void FixedUpdate()
    53.         {
    54.  
    55.             currentTime += Time.fixedDeltaTime;
    56.             if (currentTime >= realTickRate)
    57.             {
    58.                 for (int i = currentGroup; i < entities.Length; i += numberOfGroups)
    59.                 {
    60.  
    61.  
    62.                     singleTaskBehaviour.entity = entities[i];
    63.                     if (singlePandaBehavior != null && singleTaskBehaviour.entity != null && singlePandaBehavior.snapshot != null && singleTaskBehaviour.entity.snapshot.nodeStates != null)
    64.                     {
    65.                             singlePandaBehavior.snapshot = singleTaskBehaviour.entity.snapshot;
    66.                     }
    67.  
    68.                     singlePandaBehavior.Tick();
    69.  
    70.                     if (singlePandaBehavior != null && singleTaskBehaviour.entity != null && singlePandaBehavior.snapshot != null && singleTaskBehaviour.entity.snapshot.nodeStates != null)
    71.                     {
    72.                             singleTaskBehaviour.entity.snapshot = singlePandaBehavior.snapshot;
    73.                     }
    74.                 }
    75.                 Debug.Log("Test4 group: " + currentGroup.ToString() + " hits: " + hits.ToString());
    76.                 currentGroup = currentGroup == numberOfGroups - 1 ? 0 : currentGroup + 1;
    77.                 currentTime = 0f;
    78.  
    79.             }
    80.  
    81.         }
    82.  
    83.         public static void hit(int val)
    84.         {
    85.             hits += val;
    86.         }
    87.     }
    88. }
    89.  
    90.  
    and might work... do a test with only 1 entity, save its snapshot to a playerprefs, stop, play and reload the snapshot
     
  7. furroy

    furroy

    Joined:
    Feb 24, 2017
    Posts:
    93
    I've been evaluating PandaBT the last day or so. I had a use case where I wanted a guard to occasionally stand around idle for awhile, but if they spot a criminal they should immediately give chase. So wait for X seconds but abort this wait early under some conditions. Here's what I eventually came up with, but I'm not going to lie this was very non-intuitive. Did I miss some easier way of doing this?


    tree("Idle")
    random
    sequence
    DoAnim "Walk"
    Move 1 0
    sequence
    DoAnim "Walk"
    Move -1 0
    sequence
    DoAnim "Walk"
    Move 0 1
    sequence
    DoAnim "Walk"
    Move 0 -1
    sequence
    DoAnim "Idle"
    race
    not
    repeat
    not
    ShouldChase
    Wait 12.0

     
  8. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    Panda can get super convoluted if you are not careful.
    I'd do it this way:
    Code (CSharp):
    1. while
    2.    not ShouldChase
    3.    tree("Idle Wander")
    4.  
     
  9. furroy

    furroy

    Joined:
    Feb 24, 2017
    Posts:
    93
    Thanks for responding, but I don't see how that helps? What I want is to interrupt a currently executing Wait call, unless I'm missing something your solution only avoids the wait up front.

    My next attempt this morning was disastrous, locking Unity up in an infinite loop and I had to End Task it. I'm guessing since this wait triggers task.Success() something inside of Panda gets out of whack.


    [Task]
    public void WaitNoChase(float duration)
    {
    float now = Time.time;
    float endsAtTime = now + duration;
    while (now < endsAtTime && m_ChaseObject == null)
    {
    // BROKEN - DO NOT COPY THIS CODE : D
    _bt.Wait(0.1f);
    now = Time.time;
    Task.current.debugInfo = String.Format("{00:0.##} left", (endsAtTime - now));
    }
    }
     
  10. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    did you try it?
     
  11. furroy

    furroy

    Joined:
    Feb 24, 2017
    Posts:
    93
    Ah I have now and yes that seems to work. I had what I thought was an equivalent structure earlier in the tree, but I see it was activating another part of the tree but not specifically deactivating the latter section. I see I don't have a full grasp of things yet. Thank you!
     
  12. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    while = if, yeah it's super easy to trip over in Panda, that's why I mostly copy paste patterns I've used in the past. With Eric missing we'll have to share tips so we converge on the most simple patterns.
     
  13. Greenman76

    Greenman76

    Joined:
    Jan 8, 2013
    Posts:
    6
    Hello, I'm new here and having a bit of a problem. I have two BT attached to an object called DaggerMaster. I keep getting these two errors:
    PandaScriptException: Tree "SearchAndDestroy" is not defined. in file 'MainAI.BT': line 7
    and
    PandaScriptException: Task node has 2 children. None is expected in file 'DaggerMaster.BT': line 5
    How do I get MainAI.BT to find "SearchAndDestroy" in DaggerMaster.BT? Am I missing something obvious?


    MainAI.BT

    tree("Root")
    //Kill or be dead.
    parallel
    repeat mute tree("SearchAndDestroy")
    repeat mute tree("Die")

    DaggerMaster.BT

    tree("SearchAndDestroy")
    fallback
    paralell
    repeat
    tree("AttackWhenClose")

    repeat
    tree("ChaseEnemy")
     
  14. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    At first glance I'd say you newline all over the place, panda uses newline and tabs for structure but it's hard to say because your post is nearly unreadable, you need to format your code with this
     
  15. Greenman76

    Greenman76

    Joined:
    Jan 8, 2013
    Posts:
    6
    Thanks, Here's my second attempt

    Code (CSharp):
    1. tree("Root")
    2.     //Root behaviour for all unit: Be alive or die.
    3.     parallel
    4.         repeat mute tree("SearchAndDestroy")
    5.         repeat mute tree("Die")
    Code (CSharp):
    1. tree("SearchAndDestroy")
    2.     fallback
    3.         paralell
    4.             repeat
    5.                 tree("AttackWhenClose")
    6.        
    7.             repeat
    8.                 tree("ChaseEnemy")
     
    laurentlavigne likes this.
  16. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    In the second script, remove the new line between tree and repeat. What will happen is the second BT will be valid so panda will detect the searchanddestroy tree.
     
  17. mtdev01

    mtdev01

    Joined:
    May 17, 2017
    Posts:
    10
    Hi Eric

    How should I handle "wait until done" scenarios. I have something like:

    tree("Search")
    fallback
    sequence
    SearchForSomethingToDestroy
    GoToObject
    ArrivedAtTarget
    DestroySomething


    DestroySomething takes a few seconds to complete. What is the "best" way to wait to call Succeed() from my task?

    I have been doing something like this:
    Code (CSharp):
    1.      
    2.     [Task]
    3.     void DestroySomething() {
    4.         if (Target == null) {
    5.             Task.current.Succeed();
    6.         }
    7.     }
    8.  
    Thank you
     
    Last edited: Mar 14, 2018
  18. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Hi @TheGabelle

    You can select a tree by name in C# and tick it from a task, as follow:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using Panda;
    4.  
    5.  
    6. public class Entity : MonoBehaviour {
    7.  
    8.     string job;
    9.     private PandaBehaviour _bt;
    10.  
    11.     void Start()
    12.     {
    13.         _bt = GetComponent<PandaBehaviour>();
    14.     }
    15.  
    16.     [Task]
    17.     void RunJob()
    18.     {
    19.         var tree = _bt.GetTree(job);
    20.         tree.Tick();
    21.     }
    22. }
    23.  
    A snapshot is an image of a running tree at a given time. It is useful if you want to save the current state of a tree and restore it later. You can retrieve it with the "snapshot" property of the PandaBehaviour component:
    Code (CSharp):
    1. var someSnapshot = this._bt.snapshot;
    The returned object is serializable. So you can store in a file on the disk, in a database or send it over the network, using your preferred serialize, or just keep in memory.

    When you've unserialized the snapshot back, you can just assign it to the "snapshot" property to restore the tree state.
    Code (CSharp):
    1. this._bt.snapshot = someSnapshot;
    I'm not sure why you get an error. Make sure you are assigning the snapshot to the exact same tree (I should implement an error notification in case the snapshot does not match the original tree).

    Please send your script or a minimum project reproducing the error so I can investigate what the problem is.
     
  19. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    Bug: Exception: No root tree found. tree "Root" is expected. even in manual no repeat root.

    Bug: there is tickOn but no RepeatRoot variable access from PandaBehaviour in code

    Trying to make a Utility layer, gettree and tick methods don't work as expected, "Wander" is never called even though gettree is set to wander and then Tick(). See attached package.
     

    Attached Files:

  20. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    in the package the controller script should be
    Code (CSharp):
    1.     // Update is called once per frame
    2.     void Update () {
    3.         string currentTreeName="";
    4.         float maxValue=0;
    5.         foreach (var u in utilities)
    6.         {
    7.             if (u.value * u.weight > maxValue)
    8.             {
    9.                 maxValue = u.value * u.weight;
    10.                 currentTreeName = u.tree;
    11.             }
    12.         }
    13.         var currentTree = _bt.GetTree(currentTreeName);
    14.         currentTree.Tick();
    15.     }
    16.  
    and what happens then is an error, it's expecting tree.current and that doesnt exist yet I guess, because it's in manual with no repeat root?

    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. Panda.BTNode.Tick () (at Assets/PandaBehaviour/Plugins/Core/Panda/BT/BTNode.cs:94)
    3. Panda.PandaTree.Tick () (at Assets/PandaBehaviour/Plugins/Core/Panda/BT/PandaTree.cs:51)
    4. UtilityController.Update () (at Assets/PandaBehaviour/test Utility/UtilityController.cs:36)
     
  21. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @laurentlavigne
    The "Tick On" option is about when to call of PandaBehaviour.Tick() should occur. The manual option is there in case you need to tick the whole behaviour tree at a custom rate, or at a specific time. Let's say, for instance you are making a turn base game and want to tick the tree every turn.

    It's not possible to call the tick method of Tree object from Update. This method requires a running context and can only be used from a [task] method.

    I suggest you wrap you utility method into a custom [task] and call it from the root tree in the BT script. Your update method as it is should work. Just place a [Task] attribute on it and rename it.
     
  22. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Hi @mtdev01
    I think your implementation of the DestroySomething makes perfect sense: it succeeds when the target is null (when it has been destroyed).

    If you need to put some additional delay after DestroySomething before doing something else, you could just add a Wait in sequence right after the DestroySomething task.
     
  23. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    I don't know what a running context is but your solution works perfectly.
    Could you update the documentation, and also add these comments to your code so we get contextual help?
    By the way, you'll be happy to know that Panda BT works really well with a Utility AI layer.

    There is this bug:
     
  24. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    There is definitely a bug with breakpoint, when I release the breakpoint and press pause to depause, it keeps pausing
     
  25. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    I want the sequence and TickChosenTree to run in // without impacting each other currently with this script TickChosenTree clicks every second instead of every update
    UpdateStats and UpdateBT succeed immediately
    I tried some other way but it seems that TickChosenTree was isStarting the chosen tree every frame.
    What's the secret sauce?
    Code (CSharp):
    1. tree("Root")
    2.     parallel
    3.         sequence
    4.              UpdateStats
    5.              UpdateBT
    6.              Wait(1.0)
    7.         TickChosenTree
    Code (CSharp):
    1.     PandaTree currentTree;
    2.     [Task]
    3.     void UpdateBT () {
    4.         string currentTreeName="";
    5.         float maxValue=0;
    6.         foreach (var u in utilities)
    7.         {
    8.             var value = u.value * u.weight;
    9.             u.resultingValue = value;
    10.             if (value > maxValue)
    11.             {
    12.                 maxValue = value;
    13.                 currentTreeName = u.tree;
    14.             }
    15.         }
    16.         currentTree = _bt.GetTree(currentTreeName);
    17.         Task.current.Succeed();
    18.     }
    19.  
    20.     [Task]
    21.     void TickChosenTree()
    22.     {
    23.         currentTree.Tick();
    24.         Task.current.Succeed();
    25.     }
     
  26. dju4ic

    dju4ic

    Joined:
    Dec 22, 2012
    Posts:
    2
    I am having an issue with PandaBT, I have no code to share since its an easy testing scenario.. If I run Destroy() through 5k GameObjects with a blank (default) PandaBT it results in a ~4000ms+ lockup to destroy all objects.. If I take PandaBT off, and run the destroy I'm getting ~160ms.

    Is there a better method? does Panda just have too much going on while being disabled?

    *Edit* Just to confirm, its not the Destroy method being used that is the culprit, as the same results are seen using a messaging system, and iterative deletion from parent.
     
  27. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    This one looks like a bug:
    Code (CSharp):
    1. tree("Root")
    2.     //Controll movement, aiming, firing and dying.
    3.     fallback
    4.         tree("Follow Boss")
    5.         tree("Process Command Target")
    6.         tree("Wander")
    7.         tree("Die")
    8.  
    9. tree ("Wander")
    10.     while
    11.         sequence
    12.             IsHealthy
    13.             not HasBoss
    14.             SpendLessTimeThan(5.0)
    15.         sequence
    16.             Stop
    17.             Wait(.5)
    18.             SetSpeed_Slow
    19.             SetDestination_Random_Vision
    20.             MoveTo_Destination
    21.             Random
    22.                 Wait(1.0)
    23.                 Wait(2.0)
    24.                 Wait(4.0)
    Code (CSharp):
    1.     [Task]
    2.     bool SpendLessTimeThan(float maxTime)
    3.     {
    4.         if (Task.current.isStarting)
    5.         {
    6.  
    7.             Task.current.item = Time.time + maxTime;
    8.             return true;
    9.         }
    10.         else
    11.             return Time.time < (float)Task.current.item;
    12.     }
    isStarting is true on all ticks instead of, like the documentation says, "on the first tick".
    Unless "While repeats the condition" means that it resets the tick isStarting each time - the documentation needs to detail this for each function or at least esplain the inner working of isStarting and exceptions.
    If it's not a bug,what's the way to do what isStarting does in a while Task?
     
    Last edited: Mar 20, 2018
  28. Deleted User

    Deleted User

    Guest

    Hey, and thank you for the great project!

    As a newcomer to BTs I must admit I've found it somewhat hard to wrap my head around them and make them do exactly what I want (I'm coming from event-based programming background). Well, anyway, this was up until I realized that
    while
    node is in fact a "stop-executing-children-at-any-time-based-on-a-condition" node — which was by far the most difficult thing to realize (I'm not saying this is counter-intuitive — just something I didn't expect).

    In fact, this quality of
    while
    node is a game changer, since it makes long-running children "cancellable" based on a condition — and you obviously don't see this kind of thing in a traditional flow control. I imagine this deserves a special callout in the documentation ;)

    Anyhow, thanks for your great efforts!
     
  29. cephalo2

    cephalo2

    Joined:
    Feb 25, 2016
    Posts:
    262
    I have a question on dealing with callbacks. Is there a way to complete a task outside of it's method?

    I was thinking that Panda might be a good way to manage my network connectivity since there's a lot of conditions to navigate regarding how a connection can be reconnected.

    For example, calling NetworkMatch.CreateMatch requires a callback to tell you the result coming from the relay server. I'm wondering if I can treat that as a 'task' and finish the task depending on the result of the callback instead of the original method. Some of the Panda patch notes indicate that this is possible, but I can't find an example on how to do it.
     
    laurentlavigne likes this.
  30. tonOnWu

    tonOnWu

    Joined:
    Jun 23, 2017
    Posts:
    83
    Hi @ericbegue. This is a cool Asset.
    I'm about to purchase the pay version but first I need to ask you one thing. I'm using this asset with very small dimensional objects because they are required in an AR Game. The current issue is that a PandaBT Logo appears over all the objects that are using this. How can I hide this logo?
    I tried to play with the settings inside PandaScript icon (inside PandaBehaviour/Core/Editor/Gizmos), but nothing seems to work.
    I attached you an image. Thanks.
     

    Attached Files:

    Last edited: Apr 25, 2018
  31. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    close the panda behaviour in the inspector or zoom icons all the way down in the gizmos menu.
     
  32. tonOnWu

    tonOnWu

    Joined:
    Jun 23, 2017
    Posts:
    83
    Hi. I have a questions for both of your suggestions: What do you mean with "close the panda behaviour in the inspector"? Do you mean disable it? If that's the case I don't want to disable it. I need it. Maybe you mean other thing and if that's the case, please give more information.

    And about down in gizmos menu, I went into PandaBehaviour/Core/Editor/Gizmos/PandaScript icon, but I didn't find anything that suggest me to "zoom icons all the way down". I tried different setting but nothing has changed, for example I set: Max Size = 32 (the smaller option).

    In other hands, I changed the object from its original Scale of (0.04, 0.04, 0.04) to (1, 1, 1) and the same Panda Icon Appear. The icon is Always the same size. This is one very annoying thing.

    Thanks
     

    Attached Files:

    Last edited: Apr 25, 2018
  33. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    I also think Panda BT is appropriate to handle networking, since it is by nature asynchronous and involves describing processes spanning long period of time or stuffs going on in parallel.

    You can complete a task outside of its method (from a callback), you just need to obtain the task object and call Succeed, Fail or Complete on it.

    You can wrap your NetworkMatch.CreateMatch method into a task as follow (I don't know the exact syntax of your networking api, so adapt accordingly):
    Code (CSharp):
    1.  
    2. [Task]
    3. void CreateMatch()
    4. {
    5.     if( Task.current.isStarting )
    6.     {
    7.         var task = Task.current;
    8.         NetworkMatch.CreateMatch(
    9.         (matchSuccessfullyCreated) =>
    10.             {
    11.                 task.Complete(matchSuccessfullyCreated)
    12.             }
    13.         );
    14.     }
    15. }
    16.  
    Here are some caveats:
    • The task method is called on every tick, however you want to call NetworkMatch.CreateMatch only once. The best moment to do it is at the very first tick of the task (the only tick where Task.current.isStarting is true).
    • Don't use Task.current directly inside the callback. Task.current is contextual to the task method; its value is specific to each task method. Since the callback happen at any time and surely outside the task method, the value Task.current would be different from what you expect. That's why you need to copy its value into an intermediate variable (var task), and use that in the callback instead (that's not specific to Panda BT but something to think about in general when using callbacks).
    I hope that helped.
     
    Last edited: Apr 30, 2018
  34. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    tonOnWu likes this.
  35. Jobus

    Jobus

    Joined:
    Jun 7, 2013
    Posts:
    7
    Is there any video or gif of the in-inspector editor? I'd like to see how it works.
     
  36. tonOnWu

    tonOnWu

    Joined:
    Jun 23, 2017
    Posts:
    83
  37. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,910
    How do I assign a TextAsset at runtime? I've tried this, but it gives errors about compilation, but it works if I switch it out then in again in the inspector.

    Code (csharp):
    1. public TextAsset _randomBT;
    2.  
    3. void Spawn()
    4. {
    5.     _ai.BT.scripts = new TextAsset[1];
    6.     _ai.BT.scripts[0] = _randomBT;
    7. }
    I tried adding _ai.BT.Compile() at the end too, but it didn't work.
     
  38. tonOnWu

    tonOnWu

    Joined:
    Jun 23, 2017
    Posts:
    83
    Hi.

    I need to implement a "Wait" command but setting time dynamically on code. I tried to create my own WaitForSeconds like a Task in a BT file (calling a Coroutine to wait), but it didn't work. Do you have an example of how can I do it?

    Thanks.
     
  39. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    copy the WaitForSeconds(float time) from pandabehaviour.cs to your class, move the time parameter to be a class wide variable and add a SetWaitTime(float t) task to set time=t;
     
    ericbegue likes this.
  40. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    Is there a way to "turn on" and "turn off" scripts at runtime? Sort of like this:

    Code (CSharp):
    1. public class AIHandle : MonoBehaviour
    2. {
    3.      [SerializeField]
    4.      private PandaBehaviour script;
    5.  
    6.      public void TurnOff(){
    7.           script.TurnOff();
    8.      }
    9.  
    10.  
    11. }
    where I can pass in a PandaBehaviour Script
     
  41. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    no you can't
    what you can do instead is
    while condition
    stuff...
    or swap references to the scripts text files in the script array and call Compile()
     
  42. cephalo2

    cephalo2

    Joined:
    Feb 25, 2016
    Posts:
    262
    What I do is Tick() Manually, that way I can put a condition on it.
     
  43. kk99

    kk99

    Joined:
    Nov 5, 2013
    Posts:
    81
    HOW TO programmatically add/assign .BT.txt Scripts?


            ss.AddComponent<PandaBehaviour>();
    TextAsset[] tmp = new TextAsset[1];
    tmp[0] = GameObject.Find("ResourceContainer").GetComponent<ResourceContainer>().getTextAsset("PANDA_GIRL_AI");
    ss.GetComponent<PandaBehaviour>().scripts = tmp;


    I am getting ERROR PARSING SCRIPT. See console for details.

    IN FACT THERE ARE NO DETAILS IN THE CONSOLE.

    Maybe I need to call some RELOAD function after assigning. thanks for any help.

    The Text File is 100% found and working if I assign it through the Editor
     
    Last edited: Jul 25, 2018
  44. kk99

    kk99

    Joined:
    Nov 5, 2013
    Posts:
    81
    Has anyone ever got this work? Adding .BT.txt Scripts programmatically in C#?

    I have noticed. When you SWITCH between EDITOR and Visual Studio. Basically when the EDITOR loses FOCUS and gain FOCUS, the Panda Script magically works.

    So you can minimize your Window and maximize it and then the script start working.

    jeez, this is really annoying . . . Is the developer still supporting this Framework?

    When you have this actually deployed on Mobile, nothing works. hiding and opening an App does not work.
     
  45. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,221
    You call .Compile() after you référence the new panda script?
     
  46. Boke

    Boke

    Joined:
    Feb 8, 2014
    Posts:
    2
    Hello,
    I really like Panda BT.
    I am wondering, is it possible to change the Random weight programmatically in c#?
    Greetings
     
  47. kk99

    kk99

    Joined:
    Nov 5, 2013
    Posts:
    81

    does not work.

    Following trick works for me.

    ss.gameObject.SetActive(false);

    ss.AddComponent<PandaBehaviour>();
    TextAsset[] tmp = new TextAsset[1];
    tmp[0] = GameObject.Find("ResourceContainer").GetComponent<ResourceContainer>().getTextAsset("PANDA_GIRL_AI");
    ss.GetComponent<PandaBehaviour>().scripts = tmp;

    ss.gameObject.SetActive(true);
     
  48. Boke

    Boke

    Joined:
    Feb 8, 2014
    Posts:
    2

    Hello kk99,
    could you please contact me?
    Greetings!
    Boke
     
  49. kk99

    kk99

    Joined:
    Nov 5, 2013
    Posts:
    81
    ok I have to update.

    the Trick is working for the Unity Editor but not when deploying on an actual device . . .

    i have tried now pretty much anything and it is obviously not working
     
  50. TheRealAlakeram

    TheRealAlakeram

    Joined:
    Dec 12, 2017
    Posts:
    6
    Hey guys! I got a question so I really want to use this behaviour tree but the main issue I'm running into is the movetoplayer functions are not that efficient in the sense that it'll get the players location and then move to the location but if the player moves the ai will move to the old location.. is there a way I can keep the players location updated while the ai is reroute to him so it will actually follow the player instead of going to its last updated location? Perfect example of this issue is the tag example provided with panda bt where if your moving around really fast it will only go to the last known location, or would I have to make my own internal bt in order to do that?

    I was thinking of making a bool that would trigger a update function to run to update the players position until it finally got in or out of range, then it would set the bool to false...

    Thanks for any input!