Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

NodeCanvas - (Behaviour Trees | State Machines | Dialogue Trees)

Discussion in 'Assets and Asset Store' started by nuverian, Feb 8, 2014.

  1. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Thanks for the info and sorry about this. I will take a look as soon as I can.
     
  2. tresordif

    tresordif

    Joined:
    Aug 4, 2015
    Posts:
    10
    Hmm, all of the child nodes under the Repeat Decorator appear as neutral (uncolored) when I use it. Should it be visualizing the child statuses correctly (i.e. Green = Success and Red = Failure)? Since what seems to be happening for me is that it's resetting them. I'm not sure what could be causing this, since I have NodeCanvas up to date, and it appeared this way when using Unity 5.1.4f1 and also the newest 5.2.2f1.

    Even in your provided IfThenElse example scene, the Action on the leftmost branch (Get Input and Move) shows an uncolored Neutral return since it's under the Repeat decorator, even though I'm guessing it should return Success/Green.

    The Repeat decorator itself is correctly returning Running (yellow), but I just don't see any visualization of the child nodes. The only way I can see the Success/Failure returns of the child nodes is to use the modified Optional Decorator you provided.
     
  3. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Ah yes. In cases where the decorated node immediately returns Success/Failure, there is no time to visualize it before the Repeater re-executes the node (which in turn immediately returns Success/Failure again). I will try and modify the Repeater to be able to visualize this better.

    Thanks :)
     
  4. nemish-i-nelos

    nemish-i-nelos

    Joined:
    Sep 1, 2013
    Posts:
    6
    Thanks! You are right! It's my fault)
     
  5. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    Hey there, I was wondering how Concurrent States executed in parallel with other states? Can you make it always execute before the active state or after it? Can you give an example of how it can be used?

    And also, can a state have more transitions to the same state, or are we supposed to write our own custom condition tasks?

    Cheers
     
    Last edited: Nov 3, 2015
  6. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hello,

    Once an FSM is started, Concurrent States are executed before the "Start" state. So, Concurrent States are always executed first. Do you mean an example of why to use Concurrent State?

    One state can have only one transition to another state, but a transition can have multiple conditions through the use of a Condition List. The Condition List can have multiple condition tasks within and can be set to be true either when all conditions are true, or any condition is true, so you can have some flexibility there.There is no need to write your own condition task if that is the reason you asked for :)

    Let me know if you have any questions

    Cheers!
     
  7. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    Hey, thanks for the reply! :)

    Yeah I just wanted to know when the Concurrent State executes, it's good to know its first!
    If a Concurrent State (on repeat) or the active State execution takes more then 1 frame, how does it work then?

    Also, I can't believe I didn't see the Condition List :confused: lol

    I'm just wrapping my head around how to set up a platformer controller state machine properly. :D
    Is there anywhere I can see more complex examples?
     
  8. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hey,
    You are welcome :)

    I am not sure what you mean by your first question :)
    If a Cuncurrent State is set to repeat, as soon as it finish the action tasks assigned within, it will restart them.

    There are no more example scenes than the ones available on the website, which I suppose you already have, but by all means, if you need any help at all, or have any questions on how to achieve something, I'll be glad to answer them here or the official forums. :)

    So, dont hesitate to ask anything.

    Cheers!
     
  9. kenlem

    kenlem

    Joined:
    Oct 16, 2008
    Posts:
    1,630
    Thanks for the local blackboard code. It works like a charm.

    I'm working on a little system sort of like Fallout 3 where I load and unload rooms and maintain persistence between loads. Now with this, a character can remember that you've already spoken to them and any other details I care to save in the local blackboard of the DT.

    Now, my next issue is I want Behavior Trees and Dialouge Trees to be able to get and set values on a global blackboard. In my case, I have one main scene with that global blackboard and I load and unload other scenes with DT's and BT's that have to talk to it. This means that I don't have the global blackboard available at design time when I'm working on a DT or BT. I assume I would need a couple of actions to handle this?
     
  10. tresordif

    tresordif

    Joined:
    Aug 4, 2015
    Posts:
    10
    I seem to be getting an error from NodeCanvas when calling .Tick() on a BehaviourTreeOwner during an OnEnable() call. This only seems to happen when trying to use a binded property, and it looks like the value of the property on the blackboard is not yet set (null), even though the value is properly set outside of the blackboard.

    Is there any function or way to force the binded properties on the blackboard to update immediately? I'm not really sure if this would solve the problem, but it's my best guess right now.

    Edit: Solved it! Dug into the code a bit more and wrote a public blackboard function that allows access to calling _blackboard.InitializePropertiesBinding(propertiesBindTarget, false). This fixed the null property references
     
    Last edited: Nov 5, 2015
  11. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hey,
    I'm glad it works for you :)

    Global Blackboards are ment to be the same between different scenes and that is why they are not destroyed when another scene is loaded. What I tent to do is to have a prefab of the global blackboards used in the game and then add this prefab in all scenes that I require them.
    2015-11-05_19h04_16.png

    When a scene is loaded, global blackboards will persist (DontDestroyOnLoad) and any global blackboards with the same name on the new scene will be destroyed. In effect replacing them.
    This way, you can have the global blackboards available at design time.

    Is this what you are asking and if yes, does this works for you?
    Let me know if you need any clarification.

    Cheers!


    Hey,

    The blackboard variables are binded in Awake, so I don't think that is the problem, unless I misundertood it of course.
    I just checked calling Tick in a script's OnEnable while using a bound property and got no error by the way.
    Can you please post the error?

    Thanks!
     
  12. tresordif

    tresordif

    Joined:
    Aug 4, 2015
    Posts:
    10
    I actually edited my post just a few minutes before you posted to say that I solved it. I wrote a public blackboard function that allows access to calling _blackboard.InitializePropertiesBinding(propertiesBindTarget, false).

    To give you some more info on the original error, it seems dependent on the order in which different component scripts run.

    The error was being caused because the component which contains the Tick() call in OnEnable was being called first, and then the Blackboard Awake() function was being called after. If the Blackboard component is initialized first then there seems to be no problem. Changing the Script Execution Order to force the Blackboard script to run first also fixes the problem.

    I could have sworn that Unity used to call all Awake() functions first in the past, but now the order seems to be each component will call Awake() then OnEnable() before moving to the next component.
     
  13. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Thanks for the info. Since it helps, I will add a public method for initializing the variables :)
    Glad it's solved for you.

    Cheers!
     
  14. kenlem

    kenlem

    Joined:
    Oct 16, 2008
    Posts:
    1,630
    Seems a little clunky to have all those blackboards in different scenes and such. My setup is that there is one main scene that stays load and the other scenes are loaded and unloaded additively. What I'm trying to do is write actions that get and set values in the Global blackboard. I have the set working but have errors in the get version.

    Code (CSharp):
    1. using NodeCanvas.Framework;
    2. using ParadoxNotion.Design;
    3. using UnityEngine;
    4.  
    5.  
    6. namespace NodeCanvas.Tasks.Actions{
    7.     [Category("✫ Blackboard")]
    8.     [Description("Use this to set a variable on a global blackboard")]
    9.     public class SetGlobalBlackboardVariable : ActionTask {
    10.  
    11.         [RequiredField]
    12.         public BBParameter<string> globalBlackboardName;
    13.         public BBParameter<string> targetVariableName;
    14.         public BBParameter newValue;
    15.      
    16.         protected override string info{
    17.             get {return string.Format("<b>{0}</b> = {1}", targetVariableName.ToString(), newValue != null? newValue.ToString() : ""); }
    18.         }
    19.  
    20.         protected override void OnExecute(){
    21.             // (agent as IBlackboard).SetValue(targetVariableName.value, newValue.value);
    22.             GameObject globalBlackboardGO = GameObject.Find(globalBlackboardName.value);
    23.             if (globalBlackboardGO != null) {
    24.                 GlobalBlackboard globalBlackboard = globalBlackboardGO.GetComponent<GlobalBlackboard>() as GlobalBlackboard;
    25.                 if (globalBlackboard != null) {
    26.                     globalBlackboard.SetValue(targetVariableName.value, newValue.value);
    27.                 }
    28.             }
    29.             EndAction();
    30.         }
    31.      
    32.         ////////////////////////////////////////
    33.         ///////////GUI AND EDITOR STUFF/////////
    34.         ////////////////////////////////////////
    35.         #if UNITY_EDITOR
    36.      
    37.         protected override void OnTaskInspectorGUI(){
    38.             DrawDefaultInspector();
    39.             if (GUILayout.Button("Select Type"))
    40.                 EditorUtils.ShowPreferedTypesSelectionMenu(typeof(object), (t)=> {newValue = BBParameter.CreateInstance(t, blackboard);} );
    41.         }
    42.      
    43.         #endif
    44.     }
    45. }
    I'm not sure what I have to do to get the value back from the global blackboard and store in local blackboard. I keep getting an exception that it can't parse the object type. Any suggestions on what I'm doing wrong?

    Code (CSharp):
    1. using NodeCanvas.Framework;
    2. using ParadoxNotion.Design;
    3. using UnityEngine;
    4.  
    5.  
    6. namespace NodeCanvas.Tasks.Actions{
    7.  
    8.     [Name("Get Global Blackboard Variable")]
    9.     [Category("✫ Blackboard")]
    10.     public class GetGlobalBlackboardVariable : ActionTask {
    11.      
    12.         [RequiredField]
    13.         public BBParameter<string> globalBlackboardName;
    14.         public BBParameter<string> globalVariableName;
    15.  
    16.         [BlackboardOnly]
    17.         public BBParameter<object> variable;
    18.          
    19.         protected override void OnExecute(){
    20.             GameObject globalBlackboardGO = GameObject.Find(globalBlackboardName.value);
    21.             if (globalBlackboardGO != null) {
    22.                 GlobalBlackboard globalBlackboard = globalBlackboardGO.GetComponent<GlobalBlackboard>() as GlobalBlackboard;
    23.                 if (globalBlackboard != null) {
    24.                  
    25.                     //globalBlackboard.GetValue<BBParameter>(globalVariableName.value);
    26.                  
    27.                     /*
    28.                     BBParameter globalVariable = globalBlackboard.GetValue<BBParameter>(globalVariableName.value);
    29.                     if (globalVariable != null) {
    30.                         variable.value = globalVariable.value;
    31.                     } else {
    32.                         Debug.Log("No value found for " + globalVariableName.value);
    33.                     }*/
    34.                  
    35.                  
    36.                 }
    37.             }
    38.          
    39.             EndAction();
    40.         }
    41.     }
    42. }
     
  15. kenlem

    kenlem

    Joined:
    Oct 16, 2008
    Posts:
    1,630
    I found the problem. Doe!

    Code (CSharp):
    1. using NodeCanvas.Framework;
    2. using ParadoxNotion.Design;
    3. using UnityEngine;
    4.  
    5.  
    6. namespace NodeCanvas.Tasks.Actions{
    7.  
    8.     [Name("Get Global Blackboard Variable")]
    9.     [Category("✫ Blackboard")]
    10.     public class GetGlobalBlackboardVariable : ActionTask {
    11.        
    12.         [RequiredField]
    13.         public BBParameter<string> globalBlackboardName;
    14.         public BBParameter<string> globalVariableName;
    15.  
    16.         [BlackboardOnly]
    17.         public BBParameter<object> variable;
    18.            
    19.         protected override void OnExecute(){
    20.             GameObject globalBlackboardGO = GameObject.Find(globalBlackboardName.value);
    21.             if (globalBlackboardGO != null) {
    22.                 GlobalBlackboard globalBlackboard = globalBlackboardGO.GetComponent<GlobalBlackboard>() as GlobalBlackboard;
    23.                 if (globalBlackboard != null) {
    24.                     Variable globalVariable = globalBlackboard.GetVariable(globalVariableName.value);
    25.                     if (globalVariable != null) {
    26.                         variable.value = globalVariable.value;
    27.                     } else {
    28.                         Debug.Log("No value found for " + globalVariableName.value);
    29.                     }
    30.                    
    31.                    
    32.                 }
    33.             }
    34.            
    35.             EndAction();
    36.         }
    37.     }
    38. }
     
    hopeful likes this.
  16. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    @kenlem
    Hey,

    Here are the actions you are after, slightly different and with a bit more direct approach if you want to. Basicaly the GlobalBlackboards are already cached and you can get one by its name, by the static method GlobalBlackboard.Find(string name) :

    Code (CSharp):
    1. using NodeCanvas.Framework;
    2. using NodeCanvas.Framework.Internal;
    3. using ParadoxNotion.Design;
    4.  
    5. namespace NodeCanvas.Tasks.Actions{
    6.  
    7.     [Category("✫ Blackboard")]
    8.     public class SetGlobalBlackboardVariable : ActionTask {
    9.  
    10.         [RequiredField]
    11.         public BBParameter<string> globalBlackboardName;
    12.         [RequiredField]
    13.         public BBParameter<string> targetVariableName;
    14.      
    15.         public BBObjectParameter newValue;
    16.  
    17.         protected override string info{
    18.             get {return string.Format("{0} = {1}", targetVariableName.ToString(), newValue != null? newValue.ToString() : ""); }
    19.         }
    20.  
    21.         protected override void OnExecute(){
    22.             var bb = GlobalBlackboard.Find(globalBlackboardName.value);
    23.             if (bb == null){
    24.                 EndAction(false);
    25.                 return;
    26.             }
    27.  
    28.             bb.SetValue(targetVariableName.value, newValue.value);
    29.             EndAction(true);
    30.         }
    31.  
    32.  
    33.         ////////////////////////////////////////
    34.         ///////////GUI AND EDITOR STUFF/////////
    35.         ////////////////////////////////////////
    36.         #if UNITY_EDITOR
    37.      
    38.         protected override void OnTaskInspectorGUI(){
    39.             DrawDefaultInspector();
    40.             if (UnityEngine.GUILayout.Button("Select Type"))
    41.                 EditorUtils.ShowPreferedTypesSelectionMenu(typeof(object), (t)=> {newValue.SetType(t);} );
    42.         }
    43.      
    44.         #endif
    45.     }
    46.  
    47.  
    48.     [Category("✫ Blackboard")]
    49.     public class GetGlobalBlackboardVariable : ActionTask {
    50.  
    51.         [RequiredField]
    52.         public BBParameter<string> globalBlackboardName;
    53.         [RequiredField]
    54.         public BBParameter<string> targetVariableName;
    55.  
    56.         [BlackboardOnly]
    57.         public BBParameter<object> saveAs;
    58.  
    59.         protected override string info{
    60.             get { return string.Format("{0} = {1}", saveAs.ToString(), targetVariableName.ToString() ); }
    61.         }
    62.  
    63.         protected override void OnExecute(){
    64.             var bb = GlobalBlackboard.Find(globalBlackboardName.value);
    65.             if (bb == null){
    66.                 EndAction(false);
    67.                 return;
    68.             }
    69.  
    70.             saveAs.value = bb.GetValue<object>(targetVariableName.value);
    71.             EndAction(true);
    72.         }
    73.     }
    74. }

    Also, you could implement this with generics if you want to. Here are the same actions with generics, which removes the need to alter the inspector and is a bit more elegant in my opinion, just to showcase how you could do that if you want or for a future reference :)
    Code (CSharp):
    1. namespace NodeCanvas.Tasks.Actions{
    2.  
    3.     [Category("✫ Blackboard")]
    4.     public class SetGlobalBlackboardVariable<T> : ActionTask {
    5.  
    6.         [RequiredField]
    7.         public BBParameter<string> globalBlackboardName;
    8.         [RequiredField]
    9.         public BBParameter<string> targetVariableName;
    10.      
    11.         public BBParameter<T> newValue;
    12.  
    13.         protected override string info{
    14.             get {return string.Format("{0} = {1}", targetVariableName.ToString(), newValue != null? newValue.ToString() : ""); }
    15.         }
    16.  
    17.         protected override void OnExecute(){
    18.             var bb = GlobalBlackboard.Find(globalBlackboardName.value);
    19.             if (bb == null){
    20.                 EndAction(false);
    21.                 return;
    22.             }
    23.  
    24.             bb.SetValue(targetVariableName.value, newValue.value);
    25.             EndAction(true);
    26.         }
    27.     }
    28.  
    29.  
    30.     [Category("✫ Blackboard")]
    31.     public class GetGlobalBlackboardVariable<T> : ActionTask {
    32.  
    33.         [RequiredField]
    34.         public BBParameter<string> globalBlackboardName;
    35.         [RequiredField]
    36.         public BBParameter<string> targetVariableName;
    37.  
    38.         [BlackboardOnly]
    39.         public BBParameter<T> saveAs;
    40.  
    41.         protected override string info{
    42.             get { return string.Format("{0} = {1}", saveAs.ToString(), targetVariableName.ToString() ); }
    43.         }
    44.  
    45.         protected override void OnExecute(){
    46.             var bb = GlobalBlackboard.Find(globalBlackboardName.value);
    47.             if (bb == null){
    48.                 EndAction(false);
    49.                 return;
    50.             }
    51.  
    52.             saveAs.value = bb.GetValue<T>(targetVariableName.value);
    53.             EndAction(true);
    54.         }
    55.     }
    56. }

    Cheers!
     
  17. kenlem

    kenlem

    Joined:
    Oct 16, 2008
    Posts:
    1,630

    Thanks! That works like a charm and is exactly what I needed.
     
  18. sanpats

    sanpats

    Joined:
    Aug 24, 2011
    Posts:
    343
    Is it possible to add Coroutine support within ActionTask? I do not mean StartCoroutine(), but something that will execute a coroutine and wait for the coroutine to finished before continue to execute the next section of the code. Like using IEnumerator OnUpdate() instead of void OnUpdate(). That will greatly enhance the usability of NC.

    Will this kind of code work if I substitute OnYieldableUpdate() in place of a normal OnUpdate().
    Code (CSharp):
    1. public override void OnExecute()
    2. {
    3.     StartCoroutine(OnYieldableUpdate());
    4. }
    5.  
    6. public IEnumerator OnYieldableUpdate()
    7. {
    8.     while(isRunning)
    9.     {
    10.         //Do something cool
    11.         if(end)
    12.         {
    13.             EndAction(false);
    14.             yield break;
    15.         }
    16.         else if(eventName == "xxx")
    17.             SendEvent(new EventData(eventName));
    18.         //Do more
    19.         yield return null;
    20.     }
    21. }
    The problem is I am not sure certain methods like SendEvent() and EndAction() will work within a coroutine loop.

    P.S. EndAction() works fine. I will test SendEvent() next.

    P.S.2 SendEvent() won't work on the first iteration of the coroutine above, because it is basically still in the Start() function until the first yield return null;, have to change the code to the following in order for it to work.

    Code (CSharp):
    1. public override void OnExecute()
    2. {
    3.     StartCoroutine(OnYieldableUpdate());
    4.     //Do the initialization
    5. }
    6.  
    7. public IEnumerator OnYieldableUpdate()
    8. {
    9.     yield return null;
    10.     while(isRunning)
    11.     {
    12.         if(isPaused)
    13.         {
    14.             yield return null;
    15.             continue;
    16.         }
    17.         //Do something cool
    18.         if(end)
    19.         {
    20.             EndAction(false);
    21.             yield break;
    22.         }
    23.         else if(eventName == "xxx")
    24.             SendEvent(new EventData(eventName));
    25.         //Do more
    26.         yield return null;
    27.     }
    28. }
    I think you can add a built-in support that will ease this use case by adding built-in Enumerator version of OnExecute, OnUpdate, OnStop, OnPause etc.
     
    Last edited: Nov 6, 2015
  19. kenlem

    kenlem

    Joined:
    Oct 16, 2008
    Posts:
    1,630
    Nuverian - Do you have any suggestions as to how I could use NodeCanvas to build a simple quest system?
     
  20. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    @nuverian

    Hey, what is the script execution order of the NodeCanvas Graph Owners? And is there a way to control it?
     
  21. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hey,

    Sorry for late reply.
    The thing is that if I was to add IEnumerator version for all of the methods, I will also have to call them so that they can be usefull, which will result in A LOT of unnecessary coroutine calls I'm afraid. So, I don't think this is a good solution here in my opinion :)
    By the way, I've just checked sending an event from within a coroutine and seems correct. Can you please clarify on the problem you were facing?

    Thanks!

    Hey,

    That's a really broad question :)
    What kind of features should your required quest system have? Depending on the complexity, you could use FSMs to make something like a quest system, or even create a new NC graph module from scratch (although that would require a lot of work), but I don't really know what you after.

    Let me know a bit more on the details :)
    Cheer!


    Hey,
    The GraphOwner Components are not under a specific script execution order. It's up to unity how to prioritize them like the rest of the scripts.
    Unity has a utility to specify Script Execution Order. You can use that if you want fine control.

    Is that what you are asking?
    Let me know

    Cheers!
     
  22. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    @nuverian
    Hey, yes. :)

    Also, I'm finding it really weird that there's no CompareMethod.NotEqualTo, LessOrEqual, and MoreOrEqual for numeric values, why did you decide not to implement them? Also it's weird that we're just able to Check Vector2 or 3 just by the value. And not through comparisons of the x y z fields.

    Cheers
     
  23. sanpats

    sanpats

    Joined:
    Aug 24, 2011
    Posts:
    343
    You can made a derived class for ActionTask, for example a CoroutineTask, so it will only use when a user want to use the derived task. Just a suggestion. I can make that myself, but in case you want to have it as a built-in feature.

    SendEvent() works as expected but it have to be called after the first yield return null in order to work. Before the first yield return null it won't work because it is like you call SendEvent doing OnExecute which won't work in NC.
     
  24. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hey,
    I haven't added NotEqual because all conditions can be Inverted by default. Regarding LessOrEqual and GreaterOrEqual, I can certainly add those two in there :)
    There are a lot of operations that can be done with 2 Vectors and decided to no clutter the tasks with a whole bunch of them, but do you mean a condition that returns true when all or either of the x,y,z components comparison of 2 vectors are equal, geater less etc, for example?

    Hey,
    Indeed, thats a way of doing this. I might add something like this. Thanks :)
    I will also fix SendEvent to be send by the end of the frame by default and thus work in OnExecute. Basicaly the problem is if you call SendEvent, before the relevant CheckEvent condition has been checked at least once and thus register to that event. I will fix this though.

    Thanks
     
  25. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    @nuverian
    Hey, I didn't know that conditions could be inverted. :) But yeah I just added the additional CompareMethods in your code.
    And yeah, that's what I mean about the vectors. Where you can compare them not by their exact values, but test the xyz components separately. Like: If X != 0 and Y >= 0.

    I'm finally getting the hang of it, I managed to set up a great FSM. I'm using Nested FSM and Concurrent States inside of them to handle what you can do when you're in that State. And inside the nested states I also have all the states for handling animation, audio and events. So I'm using Concurrent States as an "Update" function for the Nested State.

    Cheers
     
  26. Evgeny-Eliseev

    Evgeny-Eliseev

    Joined:
    Jan 21, 2015
    Posts:
    19
    Hello!

    If I create state like this:

    Code (CSharp):
    1. public class MyState : FSMState {
    2.  
    3.     override protected void OnInit(){
    4.         Debug.Log ("Init");
    5.     }
    6.  
    7.     override protected void OnEnter(){
    8.         Debug.Log ("Enter");
    9.     }
    10. }
    and make it Start Node. Then I have

    Code (CSharp):
    1. Enter
    2. Init
    instead of

    Code (CSharp):
    1. Init
    2. Enter
    It can be fixed by putting there an empty ActionState before MyState - but this doesn't looks right.
     
  27. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hey,
    Yes, thats the right use of Concurrent states :) They are indeed as an "update" of the FSM.
    Glad to know you are getting the hang of it.
    I will also see adding some new vector tasks in the next version as well as the <= and >= methods.

    Cheers!

    Hello,
    Indeed this is a bug. Thanks for noting.
    I've found the problem but it's a bit more complicated to fix, so I will need to look into it a bit.
    By the way, you can also model a state by creating and assigning action tasks in the action state, rather than creating custom states. The action task have methods similar to init, enter, update and exit of a state and are a bit more flexible, but by all means if you like creating custom states that's fine :)
    I will fix the issue soon.

    Thanks!
     
  28. sanpats

    sanpats

    Joined:
    Aug 24, 2011
    Posts:
    343
    A feature request: allow the use of square bracket in text. I am making an RPG with skill check in the dialogue choices. I prefer to use something like "[Stealth] blah blah blah." but I can't. May be is I type "/[Stealth/]" I can show [Stealth] instead of searching for a bb parameter.
     
  29. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hey,
    That makes sense. I made it so that if the text in square brackets is not a variable will show you the text as normal, so that it's a bit easier and avoid the extra "/" characters. I have PMed you the changes required.
    Let me know if it works for you.

    Thanks
     
  30. sanpats

    sanpats

    Joined:
    Aug 24, 2011
    Posts:
    343
    BTW Is it possible to include nested (BT, FSM, especially FlowScript) nodes in the DialogueTree system?
     
  31. chaneya

    chaneya

    Joined:
    Jan 12, 2010
    Posts:
    416
    Nuverian,

    I just recently purchased both NodeCanvas and Behavior Designer and I'm comparing the two products before I commit tons of time implementing one of them in my next game.

    - One thing I like about Behavior Designer is his use of OnDrawGizmos in scene view for common movement related conditional tasks. Can See Object task has a nice cone that represents the view distance and angle. Within Distance task has a circle that represents the magnitude/radius of the distance. This is really helpful for debugging behaviors. I apologize ahead of time if this already exists in NodeCanvas but if they exist, I can't figure out how to turn them on.
    Searching this thread, in version 1.5.3 you said,
    • Added 'OnGizmosSelected' in tasks to show custom gizmos only when the task is selected/inspected in the editor.
    So I'm thinking I'm just not selecting something correctly. I would say anything that involves a Raycast would be nice to have a gizmo representation for debugging purposes.
    - Can See Target, Target in Line of Sight, Target in View Angle, Target within Distance

    If this does not currently exist, is it something you would consider adding.

    Thanks
    Allan
     
    Last edited: Nov 15, 2015
  32. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hey,
    While it's very possible and easy to add this, I still have to figure out how they will each work in the context of a Dialogue Tree. Adding flowscripts would be straightforward. I can try implementing it tomorrow and send it to you as well as of course include it in the next version :)
    For BTs and FSMs within a Dialogue Tree though, I still need to think if they are worth it and if so, how they will work exactly in the context of a Dialogue Tree.
    If you have some suggestion let me know.

    Cheers!

    Hey,
    Thanks for getting NodeCanvas.
    Yes, there is the ability to draw gizmos within tasks, either always or only when the task is selected, but it's just that I haven't actualy added the gizmos in the included tasks. I can certainly add in the most relevant tasks in the comming version though :)
    For example, you can add this code by the end of the CanSeeTarget to get debug gizmos.
    Code (CSharp):
    1.         public override void OnDrawGizmosSelected(){
    2.             if (agent != null){
    3.                 Gizmos.DrawLine(agent.position, agent.position + offset);
    4.                 Gizmos.DrawLine(agent.position + offset, agent.position + offset + (agent.forward * maxDistance.value) );
    5.                 Gizmos.DrawWireSphere(agent.position + offset + (agent.forward * maxDistance.value), 0.1f );
    6.                 Gizmos.matrix = Matrix4x4.TRS(agent.position + offset, agent.rotation, Vector3.one);
    7.                 Gizmos.DrawFrustum(Vector3.zero, viewAngle.value, 5, 0, 1f);
    8.             }
    9.         }
    Again, I will add more task gizmos in the next version :)

    Cheers!
     
  33. chaneya

    chaneya

    Joined:
    Jan 12, 2010
    Posts:
    416
    Nuverian,

    That's great. I've updated all of the scripts with OnDrawGizmosSelected methods.

    Thanks
    Allan
     
    Last edited: Nov 16, 2015
  34. sanpats

    sanpats

    Joined:
    Aug 24, 2011
    Posts:
    343
    I think just executing the nested graph using DT blackboard will suffice for FSM (like DT action node), BT might be needed to check return result whether success or failure (like DT condition node). And, yes, FlowScript is a must. It suit the minor tasks need by DT well.

    btw Unity 5.3 has already lauched a release candidate.
     
  35. Haagndaaz

    Haagndaaz

    Joined:
    Feb 20, 2013
    Posts:
    232
    The company I work for recently bought NodeCanvas, and we are wondering if there is a way to use blackboards for cross scene referencing?
     
  36. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Glad to hear :)
    Thanks

    Hey,
    I have attached here the Dialogue Tree nested FlowScript node :)
    It works similar to how your would use a flowscript in an FSM, where the Finish node is required to continue the dialogue. The dialogue will continue according to the parameter of the Finish node (success, failure).
    The dialogue actor selected in the node also acts as the agent/self for the Flowscript if needed. So in the following example, Bob will be used in the nested flowscript.
    2015-11-17_20h33_00.png

    2015-11-17_20h32_13.png

    I will of course include it in the next version of FC along with the rest of the integration nodes.

    Thanks!

    Hello and thanks.
    Can you please clarify your question since I am not exactly sure what you are asking :) ?
     

    Attached Files:

  37. sanpats

    sanpats

    Joined:
    Aug 24, 2011
    Posts:
    343
    Nice work!

    Hope you fix Unity 5.3 NC performance and graphical bug soon.
     
  38. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Thats the next thing I am taking a look at :)
     
  39. Haagndaaz

    Haagndaaz

    Joined:
    Feb 20, 2013
    Posts:
    232
    What I mean is that I would like to be able to reference objects across different scenes using the global blackboard, then load the separate scenes together at runtime. Would like to do this so we can work better as a team, eg. I work on graphics while the programmer can setup a graph in another scene, then we merge in the end by loading both scenes at runtime
     
  40. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    @nuverian Hey, at what point in the frame execution order (http://docs.unity3d.com/Manual/ExecutionOrder.html) are Transitions checked in FSMs?

    Also, since the documentation on dynamic variables is lacking, I don't really understand how to use them properly. Can you give an example of using a dynamic variable?

    Cheers
     
    Last edited: Nov 20, 2015
  41. appymedia

    appymedia

    Joined:
    May 22, 2013
    Posts:
    95
    Hi,

    First of all great work on NC!

    I'm trying to integrate the 'EasyTouch' asset from the asset store so I can have condition tasks like 'onTouchStart', 'onTouchEnd' etc. EasyTouch itself requires you to subscribe/unsubscribe to its events to work. In the EasyTouch docs it has example code to subscribe/unsubscribe like this :-

    Code (CSharp):
    1. // Subscribe to events
    2. void OnEnable(){
    3. EasyTouch.On_TouchStart += On_TouchStart;
    4. }
    5. // Unsubscribe
    6. void OnDisable(){
    7. EasyTouch.On_TouchStart -= On_TouchStart;
    8. }
    9. // Unsubscribe
    10. void OnDestroy(){
    11. EasyTouch.On_TouchStart -= On_TouchStart;
    12. }
    13. // Touch start event
    14. public void On_TouchStart(Gesture gesture){
    15. Debug.Log( "Touch in " + gesture.position);
    16. }
    So far I have a custom condition task as follows :-

    Code (CSharp):
    1. namespace NodeCanvas.Tasks.Conditions
    2. {
    3.     [Category("EasyTouch")]
    4.     public class onTouchStart : ConditionTask
    5.     {
    6.         protected override bool OnCheck()
    7.         {
    8.             return false;
    9.         }
    10.  
    11.         // Subscribe to events
    12.         protected override string OnInit()
    13.         {
    14.             EasyTouch.On_TouchStart += On_TouchStart;
    15.             return null;
    16.         }
    17.  
    18.         // At the touch beginning
    19.         public void On_TouchStart(Gesture gesture)
    20.         {
    21.             YieldReturn(true);
    22.         }
    23.     }
    24. }
    What I can't find is any condition task methods like 'OnDestroy' or similar to unsubscribe myself from the EasyTouch event, how do I unsubscribe when the GO is destroyed or disabled? Also what's the cleanest way to get back the 'Gesture' passed into the On_TouchStart method to the graph?

    I did see that I could add the 'OnDestory' event to the MessageRouter and use the 'EventReceiver' attribute to fire it but I don't want to mess with core NC files for when I update plus I'm not really sure of the implications as my C# is rusty at best!

    Look forward to any advice.

    Pete
     
  42. Haagndaaz

    Haagndaaz

    Joined:
    Feb 20, 2013
    Posts:
    232
    Been playing around with NC a bit more and found a way to do cross scene referencing (I can explain if someone would like to know), but one thing I have found is that if I change the name of a global blackboard, I lose all of my references within my FSM graph (using asset graphs). Is this something that can be fixed?
     
  43. Haagndaaz

    Haagndaaz

    Joined:
    Feb 20, 2013
    Posts:
    232
    Also, I love Node Canvas, every time I try something new it makes me more and more excited at the possibilities! :)
     
  44. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hey,

    This is done in Update like mostly everthing else :) Have you encountered some issue?

    In regards to Dynamic Variables, they are simply a way of having variables without predefining them in the blackboard and as such keep the blackboard more clean. An example of using dynamic variables would be if you have an action list, (or a series of actions in a sequencer), and you need some temporary variables that would have meaning only in the scope of these actions there. In this case, you could use dynamic variables instead of pre-creating a blackboard variables which outside of the score of these actions would have no meaning.

    In the end, a dynamic variable means that a variable of that type and the name specified, will be created in the blackboard at runtime when that variable is required.

    Let me know if you need any clarification.
    Cheers.

    Hello and thanks a lot! I'm glad you like NC :)

    If you are working with FSMs, you can override the OnEnable and OnDisable methods of ConditionTask, which are called when the parent state of the transition Enters or Exists respectively.
    For BehaviourTrees, this is something I am working on improving as we speak. The best option right now would indeed be to add OnDestroy (even OnDisable) in the message router. Don't worry, I will add it as well so you won't have any problems when you update later.

    Regarding getting the Gesture data into the graph, the best way would be to create BBParameters and write their value based on what you want to save. For example, if you wanted to save the Gesture.position, you could do this:
    Code (CSharp):
    1. namespace NodeCanvas.Tasks.Conditions
    2. {
    3.     [Category("EasyTouch")]
    4.     public class onTouchStart : ConditionTask
    5.     {
    6.        
    7.         [BlackboardOnly]
    8.         public BBParameter<Vector2> gesturePosition;
    9.  
    10.         protected override bool OnCheck()
    11.         {
    12.             return false;
    13.         }
    14.         // Subscribe to events
    15.         protected override string OnInit()
    16.         {
    17.             EasyTouch.On_TouchStart += On_TouchStart;
    18.             return null;
    19.         }
    20.         // At the touch beginning
    21.         public void On_TouchStart(Gesture gesture)
    22.         {
    23.             gesturePosition.value = gesture.position;
    24.             YieldReturn(true);
    25.         }
    26.     }
    27. }
    Again, I am working on improvements in the matter of events :)
    Please let me know if this works for you.

    Thanks!

    Hey,
    I'm still puzzled about your cross scene reference and I'd like to know what you did if you want to share :)
    Renaming global blackboard names is an open issue right now, because GlobalBlackboards live in the scene and as such you there can't be any consistency in their reference, but I will certainly take a look at improving this as best as I can.

    Thanks!
     
  45. chaneya

    chaneya

    Joined:
    Jan 12, 2010
    Posts:
    416
    Nuverian,

    I'll second the request for EasyTouch integration. I used EasyTouch in both of my previous games and plan to use it in my next. It's a fantastic touch asset!

    I'm having problems with DoTween Integration:
    I noticed that you have DoTween built in to NodeCanvas in the Tasks/Actions/Tween folder. And you have a handful of tasks setup.

    Since there's no documentation on this, I'm kind of guessing how this works but here are the steps I took, please correct me if I'm wrong on this.
    - Imported NodeCanvas into a clean project
    - Used the Tools menu in Unity and selected DoTween Utility Panel, clicked Setup Dotween
    - In the Resources section of your NodeCanvas website, I clicked on DoTween tasks by Grofit in Unofficial Third Party Integrations.
    - I downloaded the open sourced .zip from github and according to the instructions, just dragged the folder NodeCanvas Integrations into my Assets folder in my project.
    - I immediately got the following error:
    Assets/NodeCanvas/Tasks/Actions/Tween/TweenLookAt.cs(38,21): error CS0234: The type or namespace name `Kill' does not exist in the namespace `NodeCanvas.Tasks.DOTween'. Are you missing an assembly reference?

    TweenLookAt.cs is one of the task classes in NodeCanvas that worked fine before I imported the NodeCanvas Integrations folder so I'm not sure why it would break.

    Thanks
    Allan
     
  46. appymedia

    appymedia

    Joined:
    May 22, 2013
    Posts:
    95
    Hi Nuverian,

    Many thanks for the quick and helpful response, yes makes perfect sense and will continue integrating. I may shift the EasyTouch event 'subscribe/unsubscribe' into somewhere else eventually. Just thinking that continually doing it is not too bad in an FSM as its only really happening in the state changes but in a BT on each pass it might not be a good thing from a performance standpoint. Maybe a util task that does it or something, will have a think.

    Chaneya, if I get something together I'll send it over to you so you have some EasyTouch integration, another really useful asset indeed!

    Thanks again, really awesome product, really well thought through and well supported. Can I say every post on here and on your forum is put together very politely even when some of the requests might be laboursome, really nice to see and defo not always the case with all asset authors!

    Pete
     
  47. Haagndaaz

    Haagndaaz

    Joined:
    Feb 20, 2013
    Posts:
    232
    So for cross scene references, the reason we are using them is because we have a team collaboration environment in our office where one person is working on a graphics scene, one person is working on the programming in a separate scene, and one person is working on the GUI in another separate scene. We then use Advanced Additive Scenes(asset store) so that each team member can see the other members work while also working separately, then at runtime we load all the scenes together using the async load scene operation.


    This is why we needed cross scene referencing, so we can work on the same things, but separately. The way we do this using NC is using a global blackboard in the scene where the object want to reference lives, then in the programming scene I use Advanced Additive Scenes so I can see both scenes at once and I reference the global blackboard variable that lives in my graphics scene. Then when it comes time to load the scenes, I just have to make sure that I load the scenes in the correct order, so that my global blackboards come in first, and my FSM is loaded last, then the FSM can pick up the references again from the global blackboard. This also works as a way to reference scene objects from an asset graph.
     
  48. davidkellyDigit

    davidkellyDigit

    Joined:
    Jul 2, 2015
    Posts:
    1
    Hello,
    Is there a Behaviour Tree Conditional decorator that can check a field on a preferred type object in the blackboard?
    Like if I added a Car object to the blackboard could I check just the public string car.LicencePlate = some value rather than compare to another Car instance.
    If not is there a plan to add?
     
  49. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hey,
    It does indeed looks a very good asset :)
    Regarding the DOTween issue, the problem lies in that there is a namespace conflict with the extension on the github by grofit. To fix this, please add "DG.Tweening." in front of DOTween.Kill(), so that it looks like this:
    DG.Tweening.DOTween.Kill(id);

    I will also include this in the next version to avoid the conflict.

    Thanks!

    Thanks a lot Pete. I appreciate your kind words!

    The OnEnable/OnDisable methods of the condition task are only called in the context of FSMs for now. When I change this to be called from the BTs as well, they will certainly not be called every tree traversal, but rather only when the BT begins and when it ends respectively, since you are certainly right that it would not be good performance wise.

    I will post about the changes of the new version when I've wraped them up soon :)

    Thanks again!

    Hey,
    Thanks for the detailed information.
    Sounds like a very nice workflow you've set up there.
    I'm very glad you've managed to work with global blackboards in this matter and indeed doing it this way, as far as I understood, makes the global blackboard act as a bridge to scene references. :)
    If there is anything that you'd like to suggest that may improve your workflow, let me know.

    Thanks!


    Hey,
    Yes, you can do that if Car is a component :)
    To do it, you can use the usual Check Property or Field condition tasks and then override the target agent with the variable from your blackboard. Finaly, select your field or property from the coresponding button.
    Here is a demonstration with an AudioSource and a the isPlaying property, but it would be exactly the same for Car and a field using the Check Field instead of the Check Property that is used here.
    2015-11-23_19h39_24.png

    Let me know if this helps, or you need any clarification.

    Cheers!
     
  50. Haagndaaz

    Haagndaaz

    Joined:
    Feb 20, 2013
    Posts:
    232
    The only thing is the global black board names, this would allow for a bit more flexibility, but for now we at least know to be careful with naming our global blackboards. And i'll let you know if i come across anything else along the way! :)